Abstract
Objects are often characterised by identity, behaviour and state. However, the relative importance between these can vary from problem to problem. Value patterns focus on those object patterns for which behaviour and state are more important than reference based identity, i.e. the ability to distinguish one object from another by its identity is not as significant as the role played by its content, leading to a distinction between a ValueObject and a ReferenceObject.
The ImmutableValue pattern resolves a number of implementation and optimisation issues found in concurrent systems and in languages whose object models rely exclusively on reference semantics. ImmutableValue can be considered one of the common JavaIdioms.
Context
- Small objects with simple construction for which distinct object identity is not important but value based content is.
- A language, such as Java, that supports neither value based semantics for user defined types nor a way of specifying object immutability within the type system.
- Objects shared by reference between threads in a concurrent system.
Forces
- Attributes are instance variables that represent simple values rather than associations for an object, e.g. date of birth is an attribute of a person whereas their employer is an association. To avoid changes through other references when passing attributes around they must be copied, which may incur an inappropriate overhead.
- The integrity of an associative collection may be compromised if the state of an object used as a key is modified via an aliasing reference.
- The required use of clone to copy objects that should not be aliased is potentially error prone, i.e. it is easy to forget, and it cannot be enforced automatically.
- Synchronisation of state change incurs an overhead, but is essential in guarding against RaceConditions and ensuring thread safety.
- It must be easy for the programmer to use different values, i.e. there should be no great cost in terms of the usage complexity.
Solution
- Make an object's state immutable, i.e. freeze it at construction and ensure that all instance methods are queries.
- Provide an intuitive and complete set of constructors whose construction is lightweight.
Rationale
- By definition the state of an immutable object cannot be changed, and hence will not suffer from undesirable or unpredictable change.
Resulting Context
- No need for synchronisation and no RaceCondition problems.
- Sharing without aliasing side effects and therefore no need for copying.
- Change of value is effected by replacement by another object with a different value.
- The class must be final or any given subclass must also be an ImmutableValue.
- Much more dynamic memory allocation where values are changed often. If this incurs an undesirable overhead, the FlyweightPattern can be applied as an optimisation.
Examples and Related Concepts
- The standard java.lang.String class is a value based class in the core of the language. It supports only construction and query methods and its instances may be shared without aliasing or threading problems. New strings are created directly with the public assistance of constructors, indirectly as the results of query methods, or via the use of the java.lang.StringBuffer class.
- FunctionalProgramming is based on pure mathematical values which are immutable.
- In C++ many immutable values are often expressed directly using the const qualifier.
- In Ada the equivalent is the use of the constant keyword.
- The ReadonlyInterface? pattern, which is related to ImmutableInterface, which seeks to separate the modifier and query operations on a class into separate interfaces so that it is clear what role an object is permitted and intended to play when passed to another as a method argument. Such a technique enforces specification requirements more clearly in the type system. This can be considered to emulate many uses of const with references and pointers in C++, constant with access types in Ada 95, and READONLY with arguments in Modula-3. Although they are clearly related, there is a fundamental difference in intent between ImmutableValue and ReadonlyInterface?: ImmutableValue addresses issues related to concurrency and aliasing for objects whose value but not identity is important; ReadonlyInterface? addresses partitioning a class interface into separate interfaces based on method side effects to support finer grained specification of intent.
- Resolving the CircleAndEllipseProblem (a.k.a. the SquareAndRectangleProblem). This is the problem that comes from taking the apparently intuitive step from "a circle is a kind of ellipse" to having a circle inherit from an ellipse. Leaving aside the issue of redundant state in the subclass, this appears to present a problem when, given an ellipse that supports general resizing, a circle is stretched along an axis: it ceases to be a circle, and yet still retains the type of a circle. The resolution is to realise that the translation from "a circle is a kind of ellipse" is imperfect: "so long as it is not modified, a circle is a kind of ellipse". Put another way, when ellipse and circle are modelled as immutable values the problem disappears. This illustrates that the problem is simply one of incorrect modelling: the domain (mathematics) does not directly model circles and ellipses as referenced mutable entities!
History
This pattern was first documented and presented in something like this form at the BCS OOPS Pattern Day, http://www.bcs-oops.org.uk/resources/BCSOOPSNL/Issue34Summer1998/Articles/Henney.html. It was then presented informally at PLoP 98, where it was workshopped and the form on this page is pretty much the result. -- KevlinHenney