tags:

views:

651

answers:

8

When designing a class, should logic to maintain valid state be incorporated in the class or outside of it ? That is, should properties throw exceptions on invalid states (i.e. value out of range, etc.), or should this validation be performed when the instance of the class is being constructed/modified ?

+11  A: 

It belongs in the class. Nothing but the class itself (and any helpers it delegates to) should know, or be concerned with, the rules that determine valid or invalid state.

John Saunders
Thank you for your reply. In this case, would I surround instantiations with try...catch's or fail "gracefully" ? For example:try { Type myType = new Type(aValue); } catch (InvalidException e) { .. } or set to some "safe" default ?
Scott Davies
No. That's not a graceful failure, that's a lie. Leave the exception alone until there's something you can _do_ about it.
John Saunders
Ok, I think I am starting to get this. I am assuming that the "safe" default is the lie, not the try..catch ? In the try..catch case, I am not "eating" the exception, I have just left the handling out and put in ..
Scott Davies
+3  A: 

Yes, properties should check on valid/invalid values when being set. That's what it's for.

Adrian Godong
+2  A: 

Generally this belongs in the class itself, but to some extent it has to also depend on your definition of 'valid'. For example, consider the System.IO.FileInfo class. Is it valid if it refers to file that no longer exists? How would it know?

Joel Coehoorn
+2  A: 

It should be impossible to put a class into an invalid state, regardless of the code outside it. That should make it clear.

On the other hand, the code outside it is still responsible for using the class correctly, so frequently it will make sense to check twice. The class's methods may throw an ArgumentException if passed something they don't like, and the calling code should ensure that this doesn't happen by having the right logic in place to validate input, etc.

There are also more complex cases where there are different "levels" of client involved in a system. An example is an OS - an application runs in "User mode" and ought to be incapable of putting the OS into an invalid state. But a driver runs in "Kernel mode" and is perfectly capable of corrupting the OS state, because it is part of a team that is responsible for implementing the services used by the applications.

This kind of dual-level arrangement can occur in object models; there can be "exterior" clients of the model that only see valid states, and "interior" clients (plug-ins, extensions, add-ons) which have to be able to see what would otherwise be regarded as "invalid" states, because they have a role to play in implementing state transitions. The definition of invalid/valid is different depending on the role being played by the client.

Daniel Earwicker
So what happens if you have a validation requirement that an entity that has a Widget must also have a Gadget and vice versa. Since you can't assign both at the same time (typically), you will have to live with an invalid state for at least a short period of time. For most purposes, I think it's sufficient that you can't **persist** an invalid state. Ephemeral objects can be invalid as long as they aren't stored.
tvanfosson
Is this in some hypothetical language where I can't define a method that takes two arguments?
Daniel Earwicker
@Earwicker -- note, that the question is tagged C# where the class' data is usually set via property accessors rather than methods. Are you arguing that in order to maintain valid state you need to have methods that set all related properties at the same time? Assuming that you allow some intermediate invalid states for the duration of the method, that is. This seems to be a rather onerous requirement. Why not let external entities build up a valid object in the same way the class methods would and defer validation until the object is persisted?
tvanfosson
...I'll grant that there are cases where you would want to be this strict, but I don't think that those cases are typical.
tvanfosson
@tvanfosson: There are conventions to deal with this: ISupportInitialize.BeginInit/.EndInit, for instance.
John Saunders
Thanks for the feedback, everyone!
Scott Davies
@tvanfosson - if the state can be modified in stages by setting properties, then by definition you are saying that the intermediate states are valid from the perspective of the code setting the properties.
Daniel Earwicker
+1  A: 

I would agree with @Joel. Typcially this would be found in the class. However, I would not have the property accessors implement the validation logic. Rather I'd recommend a validation method for the persistence layer to call when the object is being persisted. This allows you to localize the validation logic in a single place and make different choices for valid/invalid based on the persistence operation being performed. If, for example, you are planning to delete an object from the database, do you care that some of its properties are invalid? Probably not -- as long as the ID and row versions are the same as those in the database, you just go ahead and delete it. Likewise, you may have different rules for inserts and updates, e.g., some fields may be null on insert, but required on update.

tvanfosson
This is the stake of Paul Stovell, which he summed up in this article : http://www.codeproject.com/KB/cs/DelegateBusinessObjects.aspx
Mac
A: 

It depends.

If the validation is simple, and can be checked using only information contained in the class, then most of the time it's worth while to add the state checks to the class.

There are sometimes, however, where it's not really possible or desirable to do so.

A great example is a compiler. Checking the state of abstract syntax trees (ASTs) to make sure a program is valid is usually not done by either property setters or constructors. Instead, the validation is usually done by a tree visitor, or a series of mutually recursive methods in some sort of "semantic analysis class". In either case, however, properties are validated long after their values are set.

Also, with objects used to old UI state it's usually a bad idea (from a usability perspective) to throw exceptions when invalid values are set. This is particularly true for apps that use WPF data binding. In that case you want to display some sort of modeless feedback to the customer rather than throwing an exception.

Scott Wisniewski
A: 

The valid state in a class is best express with the concept of class invariant. It is a boolean expression which must hold true for the objects of that class to be valid.

The Design by Contract approach suggests that you, as a developer of class C, should guarantee that the class invariant holds:

  • After construction
  • After a call to a public method

This will imply that, since the object is encapsulated (noone can modify it except via calls to public methods), the invariant will also be satisfied at entering any public method, or at entering the destructor (in languages with destructors), if any.

Each public method states preconditions that the caller must satisfy, and postconditions that will be satisfied by the class at the end of every public method. Violating a precondition effectively violates the contract of the class, so that it can still be correct but it doesn't have to behave in any particular way, nor maintain the invariant, if it is called with a precondition violation. A class that fulfills its contract in the absence of caller violations can be said to be correct.

A concept different from correct but complementary to it (and certainly belonging to the multiple factors of software quality) is that of robust. In our context, a robust class will detect when one of its methods is called without fulfilling the method preconditions. In such cases, an assertion violation exception will typically be thrown, so that the caller knows that he blew it.

So, answering your question, both the class and its caller have obligations as part of the class contract. A robust class will detect contract violations and spit. A correct caller will not violate the contract.

Classes belonging to the public interface of a code library should be compiled as robust, while inner classes could be tested as robust but then run in the released product as just correct, without the precondition checks on. This depends on a number of things and was discussed elsewhere.

Daniel Daranas
A: 

The class really should maintain valid values. It shouldn't matter if these are entered through the constructor or through properties. Both should reject invalid values. If both a constructor parameter and a property require the same validation, you can either use a common private method to validate the value for both the property and the constructor or you can do the validation in the property and use the property inside your constructor when setting the local variables. I would recommend using a common validation method, personally.

Your class should throw an exception if it receives invalid values. All in all, good design can help reduce the chances of this happening.

Lloyd McFarlin