The attributes that make up a
VALUE OBJECT
should form a conceptual whole.
[2]
For example,
street, city, and postal code shouldn't be separate attributes of a Person object. They are part of a
single,
whole address, which makes a simpler Person, and a more coherent
VALUE OBJECT
.
[2]
The
WHOLE VALUE
pattern, by Ward Cunningham.
Figure 5.6. A
VALUE OBJECT
can give information about an
ENTITY
. It should
be conceptually whole.
Designing V
ALUE
O
BJECTS
We don't care which instance we have of a
VALUE OBJECT
. This lack of constraints gives us design
freedom we can use to simplify the design or optimize performance.
This involves making choices
about copying, sharing, and immutability.
If two people have the same name, that does not make them the same person, or make them
interchangeable. But the object representing the name is interchangeable, because only the
spelling of the name matters. A Name object can be
copied
from the first Person object to the
second.
In fact, the two Person objects might not need their own name instances. The same Name object
could be
shared
between the two Person objects (each with a pointer to the same name instance)
with no change in their behavior or identity. That is, their behavior
will be correct until some
change is made to the name of one person. Then the other person's name would change also! To
protect against this, in order for an object to be shared safely, it must be
immutable
: it cannot be
changed except by full replacement.
The same issues arise when an object passes one of its attributes to another object as an
argument or return value. Anything could happen to the wandering object while it is out of control
of its owner. The
VALUE
could be changed in a way that corrupts the owner, by violating the
owner's invariants. This problem is avoided either by making
the passed object immutable, or by
passing a copy.
Creating extra options for performance tuning can be important because
VALUE OBJECTS
tend to be
numerous. The example of the house design software hints at this. If each electrical outlet is a
separate
VALUE OBJECT
, there might be a hundred of them in a single version of a single house
plan. But if all outlets are considered interchangeable, we could share just one instance of an
outlet and point to it a hundred times (an example of
FLYWEIGHT
[Gamma et al. 1995]).
In large
systems, this kind of effect can be multiplied by thousands, and such an optimization can make
the difference between a usable system and one that slows to a crawl, choked on millions of
redundant objects. This is just one example of an optimization trick that is not available for
ENTITIES
.
The economy of copying versus sharing depends on the implementation environment. Although
copies may clog the system with huge numbers of objects, sharing can slow down a distributed
system. When a copy
is passed between two machines, a single message is sent and the copy
lives independently on the receiving machine. But if a single instance is being shared, only a
reference is passed, requiring a message back to the object for each interaction.
Sharing is best restricted to those cases in which it is most valuable and least troublesome:
When saving space or object count in the database is critical
When communication overhead is low (such as in a centralized server)
When the shared object is strictly immutable
Immutability of an attribute or an object can be declared in some languages and environments but
not in others. Such features help communicate the design decision, but they are not essential.
Many of the distinctions we are making in the model cannot be
explicitly declared in the
implementation with most current tools and programming languages. You can't declare
ENTITIES
,
for example, and then have an identity operation automatically enforced. But the lack of direct
language support for a conceptual distinction does not mean that the distinction is not useful. It
just means that more discipline is needed to maintain the rules that will be only implicit in the
implementation. This can be reinforced with naming conventions, selective documentation, and
lots of discussion
.
As long as a
VALUE OBJECT
is
immutable, change management is simple—there isn't any change
except full replacement. Immutable objects can be freely shared, as in the electrical outlet
example. If garbage collection is reliable, deletion is just a matter of dropping all references to the
object. When a
VALUE OBJECT
is designated immutable in the design,
developers are free to make
decisions about issues such as copying and sharing on a purely technical basis, secure in the
knowledge that the application does not rely on particular instances of the objects.