Practical PHP Patterns: Value Object
Join the DZone community and get the full member experience.
Join For FreeToday comes a pattern that I have wanted to write about for a long time: the Value Object. A Value Object is conceptually a small simple object, but a fundamental piece of Domain-Driven Design and of object-oriented programming.
Identity vs. equality
The definition of a Value Object, that establishes if we can apply the pattern to a class, is the following: the equality of two Value Objects is not based on identity (them being the same object, checked with the operator ===), but on their content.
An example of PHP built-in Value Object is the ArrayObject class, which is the object-oriented equivalent of an array. Two ArrayObjects are equal if they contain the same elements, not if they are the same identical object. Even if they were created in different places, as long as the contained scalars are equals (==) and the contained objects are identical (===), they are effectively "identical" for every purpose.
In PHP, the == operator is used for equality testing, while the === for identity testing. Also custom equals() method can be created.
Basically, the == operator checks that two scalars, like strings or integers, have the same value. When used on objects, it checks that their corresponding fields are equals. The === operator, when used between objects, returns true only if the two objects are actually the same (they refer to the same memory location and one reflects the changes made to another.)
Scalars, as objects
Other languages provide Value Objects also for basic values like strings and integers, while PHP limits itself to the ArrayObject and Date classes. Another example of a typical Value Object implementation is the Money class.
Continuing with the Date example, we usually don't care if two Date objects are the same, but we care instead to know if they had the exact identical timestamp in their internals (which means they are effectively the same date.)
On the contrary, if two Users are the same, they should be the same object (in the memory graph) which resides in a single memory location, or they should have an identity field (in databases) that allow us to distinguish univocally between two users.
If you and I have the same name and address in the database, we are still not the same User. But if two Dates have the same day, month, and year, they are the same Date for us. This second kind of objects, whose equality is not based on an Identity Field, are Value Objects.
For simplicity, ORMs automatically enforce the identity rules via an Identity Map. When you retrieve your own User object via Doctrine 2, you can be sure that only one instance would be created and handed to you, even if you ask for it a thousand times (obviously other instances would be created for different User objects, like mine or the Pope's one).
In Domain-Driven Design
The Value Object pattern is also a building block of Domain-Driven Design. In practice, it represents an object which is similar to a primitive type, but should be modelled after the domain's business rules. A Date cannot really be a primitive type: Date objects like Feb 31, 2010 or 2010-67-89 cannot exists and their creation should never be allowed.
This is a reason why we may use objects even if we already have basic, old procedural types. Another advantage of objects if that they can add (or prohibit) behavior: we can add traits like immutability, or type hinting. There's no reason to modelling a string as a class in PHP, due to the lack of support from the str*() functions; however, if those strings are all Addresses or Phonenumbers...
In fact, Value Objects are almost always immutable (not ArrayObject's case) so that they can be shared between other domain objects.
The immutability solves the issues named aliasing bugs: if an User changes its date of birth, whose object is shared between other two users, they will reflect the change incorrectly. But if the Date is a Value Object, it is immutable and the User cannot simply change a field. Mutating a Value Object means creating a new one, often with a Factory Method on the Value Object itself, and place it in the reference field that pointed to the old object.
In a sense, if transitions are required, they can be modelled with a mechanism similar to the State pattern, where the Value Object returns other instances of its class that can be substituted to the original reference. Garbage collecting will take care of abandoned Value Objects.
To a certain extent, we can say that Value Objects are "passed by value" even if the object reference is passed by handler/reference/pointer like for everything else. No method can write over a Value Object you pass to it.
The issues of using Value Objects extensively is that there is no support from database mapping tools yet. Maybe we will be able to hack some support with the custom types of Doctrine 2, which comprehend the native Date class, but real support (based on the JPA metadata schema) would come in further releases.
Examples
An example of Value object is presented here, and my choice fell onto the Paint class, an example treated in the Domain-Driven Design book in discussions about other patterns.
On this Value Object we have little but useful behavior (methods such as equals(), and mix()), and of course immutability.
<?php /** * Subclassing a Value Object is rare, so we cut out this scenario * immediately. * This class represent a category of paint, like 'blue' or 'red', * not a quantity. */ final class Paint { private $red; private $green; private $blue; /** * This is paint for screens... No CMYK. * @param int 0-255 * @param int 0-255 * @param int 0-255 */ public function __construct($red, $green, $blue) { $this->red = $red; $this->green = $green; $this->blue = $blue; } /** * Getters expose the field of the Value Object we want to leave * accessible (often all). * There are no setters: once built, the Value Object is immutable. * @return integer */ public function getRed() { return $red; } public function getGreen() { return $green; } public function getBlue() { return $blue; } /** * Actually, confronting two objects with == would suffice. * @return boolean true if the two Paints are equal */ public function equals($object) { return get_class($object) == 'Paint' and $this->red == $object->red and $this->green == $object->green and $this->blue == $object->blue; } /** * Every kind of algorithm, just to expanding this example. * Since the objects are immutable, the resulting Paint is a brand * new object, which is returned. * @return Paint */ public function mix(Paint $another) { return new Paint($this->integerAverage($this->red, $another->red), $this->integerAverage($this->green, $another->green), $this->integerAverage($this->blue, $another->blue)); } private function integerAverage($a, $b) { return (int) (($a + $b) / 2); } } // client code $blue = new Paint(0, 0, 255); $red = new Paint(255, 0, 0); var_dump($blue->equals($red)); var_dump($violet = $blue->mix($red));
Opinions expressed by DZone contributors are their own.
Comments