TE
科技回声
首页24小时热榜最新最佳问答展示工作
GitHubTwitter
首页

科技回声

基于 Next.js 构建的科技新闻平台,提供全球科技新闻和讨论内容。

GitHubTwitter

首页

首页最新最佳问答展示工作

资源链接

HackerNews API原版 HackerNewsNext.js

© 2025 科技回声. 版权所有。

Invalid Object Is an Anti-Pattern

59 点作者 mjbellantoni超过 9 年前

12 条评论

grandalf超过 9 年前
While this article hits on a very true aspect of validation, I think the real problem that needs to be solved is a bit deeper:<p>There are really four levels of validity that we care about:<p>1) absolutely invalid data that indicates an unanticipated system error<p>2) invalid data that the caller (or user) must fix.<p>3) invalid data that the caller&#x2F;user can optionally fix but that we wish to warn about (via UX or in the log)<p>4) invalid data that is optional and so can be made valid by deleting it.<p>I have not yet seen a system based on types&#x2F;typing that elegantly captures the four cases above. ActiveRecord validations feels like an antipattern because it is in some ways weaker than typing, but in some cases more flexible.<p>So we typically resort to ad-hoc approaches that are essentially custom code doing the same thing in many projects. ActiveRecord validations removes boilerplate and makes it easy to give user feedback for simple flat object structures with predictable semantics and low probability of logic errors. This changes with nested structures.<p>I wonder if it would be possible to do something along the lines of the Maybe monad to handle scenarios 1-4 above via pattern matching...
评论 #10812354 未加载
评论 #10812986 未加载
dasil003超过 9 年前
A lot of things in Rails are are anti-patterns in large code bases, but pragmatic in small ones. ActiveRecord itself is a prime example: when you start an app, putting your business logic directly in ActiveRecord objects works pretty well in most cases, but later on as the models proliferate and grow, you realize that some of them contain business logic which is far too complex to warrant being conflated with persistence concerns. The result is difficulty grokking the higher level business logic that crosses database table boundaries, and potentially slow tests because you can&#x27;t reliably test complex logic without hitting the database. Of course it&#x27;s easy enough to add a service layer in a Rails app, Ruby is very flexible, but there&#x27;s no convention for it so there is a high barrier for making this decision since you lose the benefits of shared patterns.<p>Some might argue this is bad and Rails got it wrong not to anticipate this problem, but I think it&#x27;s a good decision to avoid incidental complexity for the 95% of Rails apps that will never grow beyond a certain point. In fact this is a significant reason Rails was able to get traction: in 2004 Java had solid, proven solutions to all possible concerns, but the combinatorial complexity of configuring and making them all play together made simple apps take 10x as long to get working. It&#x27;s smarter to ship fast and then refactor based on real-world feedback than attempt to implement the perfect architecture before you even know if you&#x27;re designing the right solution.
评论 #10812259 未加载
talles超过 9 年前
I&#x27;m not a Rails dev (.NET actually) but I have stumbled with this question before: should I allow invalid objects to lay around my domain?<p>It may be tempting to validate as soon as the constructor&#x2F;setter is hit. And this works great for things like a name length or an email format as the article points out. The problem with this approach is when you hit a validation that is complex and&#x2F;or needs IO. What if there&#x27;s some info in the database that would allow or not you to construct the object the way you need? You would go to the database and check the info in your constructor?<p>Strike one.<p>Some validation only makes sense on a specific operation. The object you constructed, due to some business rule, may be invalid to delete but valid to update for instance. Feels more natural to validate the object with a context: is the object valid <i>to this operation</i>?<p>Strike two.<p>Not to mention the inconvenience of never being able to have invalid entity for things like integration with other systems and logging. What if you receive, from a webservice or something, a valid object format-wise but invalid from the business rule perspective? Being able to deal with an invalid instance feels more natural, when logging such exceptional behavior, inform the other system about it, etc. In this scenario of inevitable invalid data is better to handle it built into one entity rather than fighting against a bag of properties without any form at all.<p>Strike three.
评论 #10814471 未加载
评论 #10813769 未加载
评论 #10813718 未加载
评论 #10813083 未加载
评论 #10813319 未加载
abvdasker超过 9 年前
That first example is a little silly. Rails validations have little to do with instantiating objects and are mainly concerned with preventing persisted invalid state (why they are designed to run before calling &quot;save&quot;). This is incredibly useful as a safeguard against persisting bad data, which is much more harmful than simply instantiating an object with bad data. Validating objects during instantiation sounds great, but it isn&#x27;t really the purpose of ActiveRecord&#x27;s validations.<p>That said, the second example is very cool&#x2F;interesting. Anything that makes type more explicit in Ruby is a move in the right direction.
评论 #10812244 未加载
评论 #10812160 未加载
评论 #10812197 未加载
bluesnowmonkey超过 9 年前
&gt; Both libraries are using each other, which is a cool synergy [...]<p>Or an ominous circular dependency, depending on how you look at it.
评论 #10812194 未加载
评论 #10813302 未加载
JFlash超过 9 年前
The validity of an object depends on the observer. If you require all Customers to have a billing address but a person is in the middle of a multi-page checkout, should their Customer record be invalid if they&#x27;ve only filled in their email so far? What if they want to come back to the form later?<p>Rails tries to fix this with the `:on` option for validations (<a href="http:&#x2F;&#x2F;api.rubyonrails.org&#x2F;classes&#x2F;ActiveModel&#x2F;Validations&#x2F;ClassMethods.html#method-i-validate" rel="nofollow">http:&#x2F;&#x2F;api.rubyonrails.org&#x2F;classes&#x2F;ActiveModel&#x2F;Validations&#x2F;C...</a>), which goes woefully unused in Rails apps.<p>Now you can do:<p><pre><code> customer.save(context: :checking_out) # which is different than customer.save</code></pre>
scient超过 9 年前
I&#x27;m struggling to see the improvement here. The validations are harder (more confusing?) to use, require more code&#x2F;work and then apparently try hard to provide the exact same integration that the original thing provides? Sooo whats different or better now?
gue5t超过 9 年前
Functional programming phrases this as the &quot;make illegal states unrepresentable&quot; maxim.
评论 #10813086 未加载
DanielBMarkham超过 9 年前
Meh, I&#x27;m not so sure.<p>&quot;Why do we validate data? Typically, to make sure that invalid state doesn’t leak into our systems.&quot;<p>Surprisingly, no! We validate data to put all the business validation logic into one place, regardless of whether that data is being persisted or not. In fact, a good argument can be made that the most important purpose of centralized object validation is to <i>allow</i> storing objects that are out of whack -- and that our code should degrade gracefully on load when the object is invalid -- whether that load occurs from the UI, a feed, or a persistent datastore.<p>Perfectly fine to argue the other way. In that case, nope, it doesn&#x27;t make sense.
评论 #10814510 未加载
rubiquity超过 9 年前
I think I&#x27;d rather just use a language with a good type system than write Ruby in that sort of style. The value of types is greatly diminished if there isn&#x27;t a compiler to make all sorts of wonderful optimizations and find bugs for you. At least until Ruby the language implements gradual&#x2F;soft typing directly.<p>The library mentioned also raises exceptions for bad input. Unfortunately, exceptions cripple the runtime performance of Ruby so I wouldn&#x27;t consider that a smart thing to do.
评论 #10812296 未加载
saneshark超过 9 年前
I&#x27;m a strong advocate for this and to an even larger extend some of the improvements that Trailblazer project have introduced.<p>Reform, a component of Trailblazer, uses virtus for coercion and Dry::Validation methodologies described.<p>it&#x27;s worth a look for anyone who found this post enlightening.
评论 #10814411 未加载
davidbanham超过 9 年前
From the example in the article the libraries smell a lot like a reinvention of JSON schema. What am I not seeing?