I’m a big fan of immutable objects. They appeal to me for many reasons:
- They can often be used safely across threads.
- They are suitable for use in the flyweight pattern.
- They have easy to follow behaviour (at least one axis of complexity is removed: transitivity).
The immutability requires that the object be created in a consistent state. This implies that object creation must fail instead of creating an invalid object. Failure often takes the form of exceptions. I’m fine with that. In a case like this, “fail-fast” offers certain elegance. The downside to exception throwing creation methods is the burden of exception handling they put on the caller. These exceptions are harmless when modeling and testing at the unit level and in isolation. Consider the creation of a SocialInsuranceNumber object: it could throw an InvalidSocialInsuranceNumberException instead of being created with an invalid number. In many cases, this is appropriate. When you get deep into your system, you don’t want to deal with invalid data anymore. And, in fact, we would consider it exceptional that the system would continue to operate with invalid data. The problems start when building many such objects at a time, like when capturing data in a UI or extracting values from a flat file. In such situations, the headache of handling each exception individually pushed the developer to drop in a single global exceptions handler. If the user is completing a form with 10 fields, 5 of which have some data constraints, I don’t want to have to handle 5 distinct try/catch blocks just to propagate an “invalid social security number” message to the user. It would be a little nicer if I could leverage my domain object’s validation logic without having to deal with tedious, fine-grained exception handling. In fact, some would point out that it is not exceptional to have invalid data during data validation. That’s what it’s all about. A simple way to fix this is to refactor creation method logic into a public, context-free validation method. This allows the constructor to validate data upon creation and to fail-fast rather than accumulate garbage, but it also allows for an exception-free validation when appropriate. In the case of capturing user input, if the form’s data is valid, then we can ask for the creation of appropriate domain objects to service out requests.
public class SocialSecurityNumber { public SocialSecurityNumber(string number) { if (!IsValid(number)) throw new FormatException(); // parse... } public static bool IsValid(string number) { if (number == null) return false; if (string.IsNullOrWhiteSpace(number)) return false; return true; } }
As an afterthought, I would like to point out that creation methods are great in that they can return immutable Null-objects in case of object creation failure (think of the NaN for doubles in Java or Guid.Empty in .NET) instead of throwing some unpleasant exception. This does not solve many cases, but it is handy for presenting information and not dealing with many annoying checks for null.