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
  1. DZone
  2. Coding
  3. Languages
  4. Practical PHP Refactoring: Replace Data Value with Object

Practical PHP Refactoring: Replace Data Value with Object

Giorgio Sironi user avatar by
Giorgio Sironi
·
Aug. 15, 11 · Interview
Like (0)
Save
Tweet
Share
8.00K Views

Join the DZone community and get the full member experience.

Join For Free

One of the rules of simple design is the necessity to minimize the number of moving parts, like classes and methods, as long as the tests are satisfied and we are not accepting duplication or feeling the lack of an explicit concept. Thus, a rule that aids simple design is to use primitive types unless a field has already some behavior attached: we don't create a class for the user's name or the user's password; we just use some strings.

As we make progress, however, we must be able to revise our decisions via refactoring: if a field gains some logic, this behavior shouldn't be modelled by methods in the containing class, but by a new object. The code in this new class can be reused, while the containing object will change from case to case and you will end up duplicating the same methods.

Transforming a scalar value into an object is the essence of the Replace Data Value with Object refactoring. In most of the cases, a Value Object or a Parameter Object come out as a result: while DDD pursue Value Objects as concepts in the domain layer, this refactoring is more general and can be applied anywhere. For instance, in a project we started introducing Data Transfer Objects to model the data sent by the controller to a Service Layer.

Data values in PHP

In PHP, all scalar values are by nature data values as they cannot host methods:

  • string, integers, and booleans are proper scalar.
  • arrays are not scalar in the Perl or mathematical sense, but they are still a primitive type.

On the borderline, we find some simple objects used as data containers in PHP:

  • ArrayObjects.
  • SplHeap and other SPL data structures.

The classes on the borderline may host methods, but the original class is out of reach for modification, and an indirection has to be introduced."Local Extension"

Steps

  1. Create the new class: it should contain as a private field just the value you want to substitute. The methods you immediately need have to be chosen between a constructor, getters, and setters (where needed).
  2. Change the field in the containing class. Update the constructor to also create the new object and populate the field, or accept injection (a rarer case).
  3. Update the original getter to delegate to the new one.
  4. Update the original setter to delegate to the new one (where present) or to create a new object.
  5. Run tests at the functional level; the changes should be propagated to the construction phases, while the external usage should not change very much.

Example

In the initial state, magic arrays are passed around. It's very easy to build an array where a key is missing or is called incorrectly.

<?php
class ReplaceDataValueWithObject extends PHPUnit_Framework_TestCase
{
    public function testUserCanSetANewPassword()
    {
        $userService = new UserService(/* other dependencies*/);
        $userService->newPassword(array(
            'userId' => 42,
            'oldPassword' => 'gismo',
            'newPassword' => 'supersecret',
            'repeatNewPassword' => 'supersecret'
        ));
        $this->markTestIncomplete('This refactoring is about the introduction of an object; it suffices that the test does not explode.');
    }
}

class UserService
{
    public function newPassword($changePasswordData)
    {
        /* it's not interesting to do something here */
    }
}

After the introduction of an ArrayObject extension, a little type safety is ensure and we gained a place to put methods at a little cost.

<?php
class ReplaceDataValueWithObject extends PHPUnit_Framework_TestCase
{
    public function testUserCanSetANewPassword()
    {
        $userService = new UserService(/* other dependencies*/);
        $userService->newPassword(new ChangePasswordCommand(array(
            'userId' => 42,
            'oldPassword' => 'gismo',
            'newPassword' => 'supersecret',
            'repeatNewPassword' => 'supersecret'
        )));
        $this->markTestIncomplete('This refactoring is about the introduction of an object; it suffices that the test does not explode.');
    }
}

class UserService
{
    public function newPassword(ChangePasswordCommand $changePasswordData)
    {
        /* it's not interesting to do something here */
    }
}

class ChangePasswordCommand extends ArrayObject
{
}

We add methods to implement logic on this object; in this case, validation logic; in general cases, any kind of code that should not be duplicated by the different clients.
For a stricter implementation, wrap an array or another data structure (scalars, SPL objects) instead of extending ArrayObject as you gain immutability and encapsulation (but this kind of objects need little encapsulation.)

class ChangePasswordCommand extends ArrayObject
{
    public function __construct($data)
    {
        if (!isset($data['userId'])) {
            throw new Exception('User id is missing.');
        }
        parent::__construct($data);
    }

    public function getPassword()
    {
        if ($this['newPassword'] != $this['repeatNewPassword']) {
            throw new Exception('Password do not match.');
        }
        return $this['newPassword'];
    }
}

Being this a refactoring however, this is the less invasive kind of introduction of objects you can make as the client code can still use the ArrayAccess interface and treat the object as a scalar array.

Object (computer science) PHP Data (computing)

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • GitLab vs Jenkins: Which Is the Best CI/CD Tool?
  • Spring Cloud
  • HTTP vs Messaging for Microservices Communications
  • Cucumber.js Tutorial With Examples For Selenium JavaScript

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: