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: Preserve Whole Object

Practical PHP Refactoring: Preserve Whole Object

Giorgio Sironi user avatar by
Giorgio Sironi
·
Nov. 23, 11 · Interview
Like (0)
Save
Tweet
Share
6.07K Views

Join the DZone community and get the full member experience.

Join For Free

In the scenario of today, we are extracting some fields or calculated values from an object, and then calling a method somewhere else by passing them in as parameters.

The code under scrutiny has dependencies both on the object, the method to call and the internals that it has to extract. An alternative, which this refactoring leads us to, is to pass in the whole object instead.

Why a single object as parameter?

First of all, passing the object often results in clearer signature of the method as it takes an higher-level entity with respect to some scattered fields. It also correspondes to a shorter list of parameters.

The code under refactoring also shows feature envy and lack of encapsulation with respect to the whole object: it's better to expose that in a single place (thecalled method) than to rewrite it for each call (with lots of duplication.)

Fowler notes that the refactoring closes against a particular change: the case where new data is needed from the object. You won't need to change all calls in that case, just the internal code of the method and the interface of the object.

A note on dependencies

Originally, there is a dependency from the refactored code to the object pieces to extract and pass. After the refactoring, this dependency is eliminated and substituted by a dependency from the refactored method to the whole object. Choose the best case for your situation.

In case you need to establish an ugly dependency, introduce also an interface between the new method and the whole object. It makes miracles for ease of testing and reuse of code.

Alternatives

We have alternatives when the method extracts lots of things from the object: it is a case of feature envy and part of its logic should be moved on the object itself to preserve encapsulation (and avoiding exposing each single private field with a getter).

There is also the case of the whole object calling the method by passing himself. I personally do not like to pass $this instead of $this->field{1,2,3} when the method on A is called by the object B passing its own values.

But it's a matter of dependency: the question to answer on a case-by-case basis is if it's better to pass to the method of A a subset of the interface of B (calls to one or two methods with good names) or a set of scalar parameters (fields/collaborators of B).

Steps

  1. Add a new parameter to the method: the whole object. Use Add Parameter.
  2. Determine the parameters to take from the object.
  3. For each of them, replace the inside reference with code that obtains it from the whole object.
  4. Delete the parameter and restart with the next one. Use Remove Parameter to avoid breaking the code.

Remember to simplify the code surrounding the calls now that they pass the whole object: extracting parameters which are not passed anymore is not necessary.

Example

We start from a method accepting some information about a date: it is a time slot for a calendar, which is specified for a particular month. It tells the client code whether the slot will contains at least one week when starting it at a particular date. A week is defined as more than seven days.

We want to pass the date directly instead of its pieces, which are scalar values.

<?php
class PreserveWholeObject extends PHPUnit_Framework_TestCase
{
    public function testTheSlotEvaluatesItsLength()
    {
        $today = new DateTime('2011-11-23');
        $slot = new MonthSpecificSlot();
        $this->assertTrue($slot->containsAWeek($today->format('m'), $today->format('d')));
    }
}

class MonthSpecificSlot
{
    public function containsAWeek($month, $day)
    {
        $reference = new DateTime('2011-' . $month . '-01');
        $daysInMonth = $reference->format('t');
        return $day + 6 <= $daysInMonth;
    }
}

We add a new parameter: the DateTime object from which day and month are extracted. We don't need to address the dependency towards the object by introducing an interface, because DateTime is a plain ValueObject featured by the language, like (for example) ArrayIterator.

We execute the transformations for both parameters in a single step, although you may tackle one at the time in complex situations. It makes sense to batch the parameters together as you will modify the same files containing the calls for everyone of them.

<?php
class PreserveWholeObject extends PHPUnit_Framework_TestCase
{
    public function testTheSlotEvaluatesItsLength()
    {
        $today = new DateTime('2011-11-23');
        $slot = new MonthSpecificSlot();
        $this->assertTrue($slot->containsAWeek($today, $today->format('m'), $today->format('d')));
    }
}

class MonthSpecificSlot
{
    public function containsAWeek($startDate, $month, $day)
    {
        $reference = new DateTime('2011-' . $month . '-01');
        $daysInMonth = $reference->format('t');
        return $day + 6 <= $daysInMonth;
    }
}

We now extract the parameters from inside the object in the method. The test still passes.

class MonthSpecificSlot
{
    public function containsAWeek($startDate, $month, $day)
    {
        $month = $startDate->format('m');
        $day = $startDate->format('d');
        $reference = new DateTime('2011-' . $month . '-01');
        $daysInMonth = $reference->format('t');
        return $day + 6 <= $daysInMonth;
    }
}

We remove the unused parameters, by acting also on the client code.

<?php
class PreserveWholeObject extends PHPUnit_Framework_TestCase
{
    public function testTheSlotEvaluatesItsLength()
    {
        $today = new DateTime('2011-11-23');
        $slot = new MonthSpecificSlot();
        $this->assertTrue($slot->containsAWeek($today));
    }
}

class MonthSpecificSlot
{
    public function containsAWeek($startDate)
    {
        $month = $startDate->format('m');
        $day = $startDate->format('d');
        $reference = new DateTime('2011-' . $month . '-01');
        $daysInMonth = $reference->format('t');
        return $day + 6 <= $daysInMonth;
    }
}

The refactoring is finished, but there is a further refinement we can make: obtaining the number of days in the month from the original DateTime object. Mikado refactorings such as this technique enable further simplifications of the code.

class MonthSpecificSlot
{
    public function containsAWeek($startDate)
    {
        $day = $startDate->format('d');
        $daysInMonth = $startDate->format('t');
        return $day + 6 <= $daysInMonth;
    }
}
Object (computer science) PHP

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • HTTP vs Messaging for Microservices Communications
  • Practical Example of Using CSS Layer
  • How To Best Use Java Records as DTOs in Spring Boot 3
  • Stress Testing Tutorial: Comprehensive Guide With Best Practices

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: