DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
11 Monitoring and Observability Tools for 2023
Learn more
  1. DZone
  2. Coding
  3. Languages
  4. Practical PHP Patterns: Value Object

Practical PHP Patterns: Value Object

Giorgio Sironi user avatar by
Giorgio Sironi
·
Sep. 29, 10 · Interview
Like (1)
Save
Tweet
Share
22.46K Views

Join the DZone community and get the full member experience.

Join For Free

Today 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));
Object (computer science) PHP

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Stress Testing Tutorial: Comprehensive Guide With Best Practices
  • How To Create a Failover Client Using the Hazelcast Viridian Serverless
  • Microservices 101: Transactional Outbox and Inbox
  • Fixing Bottlenecks in Your Microservices App Flows

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: