Agile Zone is brought to you in partnership with:

I am a programmer and architect (the kind that writes code) with a focus on testing and open source; I maintain the PHPUnit_Selenium project. I believe programming is one of the hardest and most beautiful jobs in the world. Giorgio is a DZone MVB and is not an employee of DZone and has posted 638 posts at DZone. You can read more from them at their website. View Full User Profile

Practical PHP Refactoring: Change Reference to Value

08.22.2011
| 5381 views |
  • submit to reddit
This refactoring targets simplification: it is the opposite of Change Value to Reference. An object whose lifecycle is tracked and which must have a single instance for each different identity is replaced by a Value Object, of which multiple copies can be created at will.

When should I replace an Entity with a Value Object?

For example, when maintaining a single reference is too difficult to be worth it. Moreover, the maximum scope of an object in PHP is the request, so you already have multiple copies of an object in different instances of the PHP script that are being executed concurrently.

With a Value Object, you are able to create it directly from input data; with an Entity/Reference object, you have to do a lookup in some other object to make sure you do not create a duplicate.

Another case is when you favor immutability over changing the state of an object: a Value Object is replaced by another Value Object of the same class in case it has to change, while an Entity/Reference object has an internal state which is mutable.

Steps

  1. Check the immutability of the object. Before going further, it has to become immutable, without setters or similar methods being able to alter the content of its private fields.
  2. Add semantic methods typical of Value Objects: an appropriate constructor or Factory Methods, and an equality method where the == operator doesn't cut it (for example, when the object maintains references to other Reference objects.)
  3. Execute tests to avoid regression.

From now on you can start use the constructor or the Factory Methods in new places, ignoring the lookup you needed to perform before. Although the lookup of the "one true instance" of each object still works, it's not necessary with a Value Object and will fade away over time.

Example

In the code sample, we go from a static lookup of objects (which usually is not static but uses a Repository) of the Color class, to their naive creation as Value Objects. For example, when an HTTP request contains their value as strings, the service in charge can easily create the necessary objects, without referring to a container of instantiated colors.

Initially, the Color class has a test where the single identity of two colors requested in different moments is checked. The Color objects are already immutable, and there is no equality method since == is already working well.

<?php
class Color
{
    private $red;
    private $green;
    private $blue;
    private static $colors = array();

    private function __construct($r, $g, $b)
    {
        $this->red = $r;
        $this->green = $g;
        $this->blue = $b;
    }

    /**
* A static lookup method is common for the Flyweight pattern
* implementation, and keeps only one instance of each color around.
* An equivalent starting point would be a ColorRepository or ColorFactory
* class.
*/
    public static function getColor($r, $g, $b)
    {
        $code = $r . $g . $b;
        if (!isset(self::$colors[$code])) {
            self::$colors[$code] = new self($r, $g, $b);
        }
        return self::$colors[$code];
    }

    /* ... other methods ... */
}

class ReferenceObjectTest extends PHPUnit_Framework_TestCase
{
    public function testASingleInstanceCanBeCreated()
    {
        $blue = Color::getColor(0, 0, 255);
        $anotherBlue = Color::getColor(0, 0, 255);
        $this->assertSame($blue, $anotherBlue);
    }
}

We relax the test: we only need equality between objects, not to have a single instance of blue across the application.

class ReferenceObjectTest extends PHPUnit_Framework_TestCase
{
    public function testEqualInstancesCanBeCreated()
    {
        $blue = Color::getColor(0, 0, 255);
        $anotherBlue = Color::getColor(0, 0, 255);
        $this->assertEquals($blue, $anotherBlue);
    }
}

The relaxed implementation of the class becomes a Value Object.

class Color
{
    private $red;
    private $green;
    private $blue;

    private function __construct($r, $g, $b)
    {
        $this->red = $r;
        $this->green = $g;
        $this->blue = $b;
    }

    public static function getColor($r, $g, $b)
    {
        return new self($r, $g, $b);
    }

    /* ... other methods ... */
}

Probably now the factory method is unnecessary and can be removed, or if we want to keep around it, we can give it a better name like fromRGBDecimals().

Published at DZone with permission of Giorgio Sironi, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)