Patterns For PHP
Index Patterns News Forums

Value Object

From Patterns For PHP

Introduction

Defining a Value Object can be difficult. The simplest way to think of it is as a typical value like any integer or string. The number 10, for example, can appear in numerous objects in an application. However the number has no identity - each is its own separate entity. In contrast, many objects in an application will be reference objects. Reference objects are the result of the pass-by-reference behaviour in PHP. If you create a User_A object and pass it into an application, then you can be reasonably certain that any User_A object is identical to any other User_A object you'll encounter - in fact any change to one of these will be reflected in all of the others. All reference objects point to the exact same object held in memory. This is illustrated by the Singleton and Registry patterns.

A Value Object on the other hand has no identity. If you have two seemingly identical Value Objects, and change the value in one, the other will be unaffected. Both may hold the same value, but each is independent of the other - they are not reference objects. There can be any number of Value Object copies, just as there can be any number of integers of 10 in an application. To enforce this behaviour its important that Value Objects are not passed-by-reference. When you create a Value Object, and assign it to a second variable - each variable must hold an independent copy of the Value Object: changes to one variable will NOT effect the other.

This might sound an alarm bell since every new assignment of a Value Object creates a new copy. Since Value Objects (in general terms) are small objects this isn't as memory intensive as one might suspect. Just as you don't care how many integers of 10 exist in an application, it shouldn't matter how many Value Objects holding an integer 10 value exist.

Finally, a Value Object may be used to represent any singular value as an object. This is a fairly common practice (perhaps more likely in Java) when the value is easier to describe and identify if encapsulated in a simple object.

Implementation for PHP5

Implementing a Value Object is quite simple. The main issue to ensure a Value Object has three possible primary features:

  1. It's value is immutable, i.e. you cannot change the original value held by a Value Object
  2. It's passed-by-copy, i.e. when passed as a parameter to a method, a new copy is created
  3. It often has operators similar to the underlying type.

The first feature is the simplest. Just make the property storing the value in the class private and do not add a setter method. The second is more difficult, but not impossible. The third is what makes Value Objects interesting. Even though a Value Object may represent an Integer, this does not mean one can use the normal +/- operators. Instead these need to be added as methods in the Value Object.

Here's a simple example for a Salary value:

php
class Salary {
 
    private $salary = 0;
 
    public function __construct($amount){
        $this->salary = (float) $amount;
    }
 
    public getSalary() {
        return $this->salary;
    }
 
    public function add($amount) {
        return new Salary( $this->salary + (float) $amount );
    }
 
    public function subtract($amount) {
        return new Salary( $this->salary - (float) $amount );
    }
 
}

As we noted a key feature of a Value Object is that its value cannot be changed. The best way of ensuring immutability is to use a private or protected property to hold the value, implement a getter, and NOT add a setter. Related to this, we also want to ensure that operations do not change the current object either. When you add() or subtract() an amount from the Value Object, a new Value Object containing the result is returned.

This also illustrates why a Value Object cannot have multiple references. If we have a User A with Salary(10000) and we pass-by-reference this Value Object to another User B with the same Salary, then we introduce a possible aliasing bug. Imagine if User B is promoted and their Salary is increased to 20000. If we were able to change the value, and did so, then User A (who shares the same Salary Object) would likewise receive an unexpected bonus (before Human Resources figured it out and asked for the money back). In our above example, using the add() method solves this by returning a new Value Object instead of changing the current Value Object - User A will be uneffected by any change to User B's salary.

These behaviours ensure that any attempt to alter an existing Value Object immediately creates a new separate Value Object while leaving the original untouched (even it is referenced). Even though Value Objects can be passed-by-reference, attempts to operate on them generate new independent objects. This has the same effect as passing copies into other class methods.

As we'll see in the PHP4 example, because PHP4 does not pass-by-reference by default this actually simplifies creating and using Value Objects. It's a rare example of something that's easier to accomplish in PHP4 than it is in PHP5.

MediaWiki
GNU Free Documentation License 1.2