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
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

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
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

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Cutting-Edge Object Detection for Autonomous Vehicles: Advanced Transformers and Multi-Sensor Fusion
  • Inheritance in PHP: A Simple Guide With Examples
  • Writing DTOs With Java8, Lombok, and Java14+
  • Graph API for Entra ID (Azure AD) Object Management

Trending

  • Scaling Mobile App Performance: How We Cut Screen Load Time From 8s to 2s
  • Chaos Engineering for Microservices
  • Testing SingleStore's MCP Server
  • Simplify Authorization in Ruby on Rails With the Power of Pundit Gem
  1. DZone
  2. Coding
  3. Languages
  4. Practical PHP Patterns: Value Object

Practical PHP Patterns: Value Object

By 
Giorgio Sironi user avatar
Giorgio Sironi
·
Sep. 29, 10 · Interview
Likes (1)
Comment
Save
Tweet
Share
24.0K 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.

Related

  • Cutting-Edge Object Detection for Autonomous Vehicles: Advanced Transformers and Multi-Sensor Fusion
  • Inheritance in PHP: A Simple Guide With Examples
  • Writing DTOs With Java8, Lombok, and Java14+
  • Graph API for Entra ID (Azure AD) Object Management

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

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

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!