A business model will normally be composed of a basic case or abstraction that is specialized and/or refined to capture the diversity present in the business. However, there will often be circumstances where the inclusion of all business possibilities in the class hierarchy would be confusing, difficult or otherwise inappropriate. You will, at times need to extend the range of an attribute beyond that offered by a Whole Value.
Consider the poll taker who collects answers of the form "agree", "strongly agree", etc. Answers that defy quantification, like "illegible" or "refused" are better represented outside the range of values, no matter how fuzzy it may be. However, the structure of a domain model had best leave a place for this sort of missing data for it may appear later. In fact, missing values are impossible to avoid during the creation (data entry) of all but the most trivial domain models.
Therefore:
Use one or more distinguished values to represent the exceptional circumstances. Exceptional values should either accept all messages, answering most with another exceptional value, or reject all messages (via does not understand) with the possible exception of identifying protocol like isNil or printOn:.
Interface widgets should produce nil on blank input and produce blank given nil output.
Domain models should accept nil or other exceptional values as legal input, at least temporarily.
In Smalltalk it is possible to make refinements of UndefinedObject that can carry an explanation. If you do, note that aValue == nil no longer means the same thing as aValue isNil.
purchaseDate buys := self trades select: [:each | each isPurchase] buys size <= 1 ifTrue: [buys first settleDate] ifFalse: [ExceptionalValue reporting: 'various']
Exceptional values will simplify the domain model hierarchy and method structure. It should not be necessary to explicitly test for exceptional values in methods because they will either absorb messages or produce Meaningless Behavior.
The little exceptional value handling that is required can be concentrated extremely close to the user interface. For example, the report writer needs to detect exceptional values to correctly compute a weighted average. Properly objectified, the AveragedColumn object can perform this computation on behalf of any domain object and thereby separate concerns.
Deferred Validation is responsible for testing completeness, recognizing that Hypothetical Publication may not require completeness.
DOT FROM preview-next-diagram