Tutorials

Design Patterns - Value Object

Views: 40676
Rating: 4/5
Votes: 1

Introduction

The Value Object pattern has, just like the Singleton, to do with referencing and instances. In a way, the Value Object is the opposite of the Singleton: its goal is to ensure NOT to use the same instance, under certain conditions.

Problem

How to prevent that objects serving as containers to certain values are unintentionally reused instead of re-instantiated.

If you have read OO PHP Part 1, you probably remember Snoopy and Lassie being attached at the mouth. It is because PHP 5 uses handles instead of copying every object like PHP 4 does that the Teeth property of Lassie wasn’t duplicated, causing both dogs’ Teeth property to refer to the same Teeth object.

This is essentially what you are trying to avoid when implementing the Value Object Pattern.

Solution

In Patterns of Enterprise Application Architecture, Martin Fowler makes a clear distinction between ‘reference objects’ and ‘Value Objects’. His short definition of a Value Object:

“A small simple object, like a money or date range, whose equality is not based on identity.”

The key being the equality. Normally, all objects persist and have their own identity, ignorant to equality of their content. An object should be a Value Object if it it’s equality is not based on identity, but on it’s ‘value’. If it is safe to equal óne hundred kudos to another hundred kudos, a Value Object – possibly called ‘Kudos’ - is warranted.

The below code is a scenario in which a Value Object is needed:

/**
 * A toolbox. Unfortunately we only carry nails.
 *
 */
class ToolBox 
{
	/**
	 * A Nails object
	 *
	 * @var Nails
	 */
	private $_nails;
	
	/**
	 * Get the nails
	 *
	 * @return Nails
	 */
	public function getNails()
	{
		return $this->_nails;
	}
	
	/**
	 * Set the nails
	 *
	 * @param Nails $nails
	 */
	public function setNails(Nails $nails)
	{
		$this->_nails = $nails;
	}
	
}

/**
 * Object representing a quantity of nails
 *
 */
class Nails 
{
	/**
	 * The quantity of nails
	 *
	 * @var int
	 */
	private $_quantity;

	/**
	 * Constructor
	 *
	 * @param int $quantity
	 */
	public function __construct($quantity) 
	{
		$this->_quantity = (int) $quantity;
	}
	
	/**
	 * Add some nails
	 *
	 * @param Nails $quantity
	 */
	public function add(Nails $nails) 
	{
		$this->_quantity += $nails->count();	
	}
	
	/**
	 * Get the nail-count
	 *
	 * @return int
	 */
	public function count() 
	{
		return $this->_quantity;
	}
	
	/**
	 * Get a string representation
	 *
	 * @return string
	 */
	private function __toString()
	{
		return (string) $this->_quantity;
	}

}
$myToolBox = new ToolBox;
$yourToolBox = new ToolBox;

//Using twenty Nails
$twentyNails = new Nails(20);

//Start out with equal number of nails.
$myToolBox->setNails($twentyNails);
$yourToolBox->setNails($twentyNails);

//Here's another 100 nails.
$yourToolBox->getNails()->add(new Nails(100));

echo "Your nails: {$yourToolBox->getNails()}<br/>";
echo "My nails: {$myToolBox->getNails()}<br/>";

You probably already noticed that problem is that we are both using the same Nails object. In this case the problem may be easy to spot and avoid, but as your application becomes bigger, preventing this type of mishap can save you a huge headache. Another mayor benefit of using Value Objects is they enable you to encapsulate type-specific operations. Martin Fowler does a great job at demonstrating this with his Money pattern, which encapsulates the handling of rounding currency.

The key to creating Value Objects is making them immutable. Because Value Objects’ equality don’t depend on their identity, simply creating a new object when the value changes, accomplishes this:

/**
	 * Add some nails
	 *
	 * @param Nails $quantity
	 */
	public function add(Nails $nails) 
	{
		return new Nails($this->_quantity + $nails->count());	
	}

Now that Nails is immutable, the client code needs to be adapted also. Since the value of the object in the $screws property can not be changed, we’ll have to reset it instead:

//Here's another 100 nails.
$yourToolBox->setNails(
	$yourToolBox->getNails()->add(new Nails(100))
);

That's all there is to it.