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 637 posts at DZone. You can read more from them at their website. View Full User Profile

Practical PHP Refactoring: Remove Parameter

11.09.2011
| 3914 views |
  • submit to reddit

As Kent Beck says, all refactoring techniques are bidirectional; in the case of add and remove refactorings, one direction increases the moving parts to face with complexity while the other simplifies the design after some responsibilities have been clarified or eliminated.

Why removing a parameter?

Of course, you shouldn't remove a parameter if you're using it. When it is out of place, you should first arrive at a situation where it is no longer referenced inside a method: this refactoring is about its subsequent removal and its possible pitfalls.

Once a parameter is not used anymore, deleting it means you'll have less complexity to worry about: method calls will be easier to perform; they will be easier to understand when read by your colleagues; and debugging or modifying the method body will be easier without adding to your mental load another variable to keep track of.

In case you're struggling with long parameter lists when no one of them can be easily removed, there are other refactorings we will encounter thay may help you consolidate the list in fewer objects.

Steps

Fowler's steps target a static language, so I will tune them to PHP's situation.

  1. Check polymorphic implementations, such as superclasses and subclasses. The method should be modified in every place where it is overridden or derived from.
  2. Following a PHP/JavaScript style, you can start by removing the parameter from the signature. The calls will still work, but the parameter will be ignored.
  3. Afterwards, you can edit calls by removing the actual parameters.

The tests suite should of course be green at the end of the refactoring.

Example

We start from the previous scenario, where we had just added an explicit parameter representing the data of the new invoice to insert. The requirement for progressive numbers was that invoices are maintained in chronological order.

<?php
class RemoveParameter extends PHPUnit_Framework_TestCase
{
    public function testProvidesNextInvoiceNumbersForTheCurrentDate()
    {
        $invoices = new Invoices(array(1 => '2011-10-01', 2 => '2011-11-01'));
        $this->assertEquals(3, $invoices->getProgressiveNumberForInsertion('2011-11-02'));
    }

    public function testNextInvoiceNumberIsAnExistingOneInCaseWeHaveToRenumerate()
    {
        $invoices = new Invoices(array(1 => '2011-10-01', 2 => '2011-11-10'));
        $this->assertEquals(2, $invoices->getProgressiveNumberForInsertion('2011-11-02'));
    }
}

class Invoices
{
    private $invoiceDates;

    public function __construct($invoiceDates)
    {
        $this->invoiceDates = $invoiceDates;
    }

    public function getProgressiveNumberForInsertion($currentDate)
    {
        foreach ($this->invoiceDates as $number => $date) {
            if ($date > $currentDate) {
                $nextNumber = $number;
                break;
            }
        }
        if (!isset($nextNumber)) {
            $nextNumber = count($this->invoiceDates) + 1;
        }
        return $nextNumber;
    }
}

Now we encounter a simplification in requirements: invoices will be added to this system just as they are issued. Thus we don't have the responsibility of calculating the right point in the list where to insert them:

class Invoices
{
    private $invoiceDates;

    public function __construct($invoiceDates)
    {
        $this->invoiceDates = $invoiceDates;
    }

    public function getProgressiveNumberForInsertion($currentDate)
    {
        $nextNumber = count($this->invoiceDates) + 1;
        return $nextNumber;
    }
}

Since the parameter is not needed anymore, we start the refactoring. First, we remove the formal parameter: any actual parameter passed in will be ignored.

class Invoices
{
    private $invoiceDates;

    public function __construct($invoiceDates)
    {
        $this->invoiceDates = $invoiceDates;
    }

    public function getProgressiveNumberForInsertion()
    {
        $nextNumber = count($this->invoiceDates) + 1;
        return $nextNumber;
    }
}

Then we adjust the calls. Due to the simplification in requirements, only a single test is now needed for this feature.

This kind of test code should be unified before simplifying the method as we did in the first step, I agree. However, test code in this article series represents not only the test suite but also all the client code which calls the code undergoing the refactoring; my argument is that while removing a parameter, client code should be simplified after the called one. The reason is simply that PHP supports calls that provides additional arguments, but not calls that provides not enough arguments.

<?php
class RemoveParameter extends PHPUnit_Framework_TestCase
{
    public function testProvidesNextInvoiceNumbersForTheCurrentDate()
    {
        $invoices = new Invoices(array(1 => '2011-10-01', 2 => '2011-11-01'));
        $this->assertEquals(3, $invoices->getProgressiveNumberForInsertion());
    }
}

Now we're finished. If you have doubts about having fixed all the method calls, temporarily transform the signature into something like this:

    public function getProgressiveNumberForInsertion(InexistentClass $shouldNotBePassed = null)

and run the test suite. Any call which passes an argument (different from null) will result in an error detected by PHPUnit.

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