Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Practical PHP Refactoring: Inline Temp

DZone's Guide to

Practical PHP Refactoring: Inline Temp

· Agile Zone
Free Resource

See how three solutions work together to help your teams have the tools they need to deliver quality software quickly. Brought to you in partnership with CA Technologies

Inline Temp is a refactoring whose purpose is to substitute a temporary variable with the expression that was used to calculate it or access its value.

While for methods we started seeing the extraction first, and then the complementary Inline refactoring, for temps we'll follow the opposite order. The complementary design for a Temp variable is a Query, which is instead a method, encapsulating for example whether the value is calculated or stored.

Why?

Inline Temp is commonly used as a starting point for other refactorings. In fact, Inline refactorings have always two possible reasons for happening:

  • undoing a previous extraction, making you free to start extracting again in different places.
  • undoing a previously introduced abstraction, which has become too thin to be useful.

In the case of a Temp, in the latter case often you throw away a Query method which just returns a value which would already be accessible to the client code.

The former case is more interesting: I often inline a Temp calculation because I want to change its behavior in different places. With TDD you may start with a single Temp that is copied in various places to produce a result, but after adding more and more tests substitute it with different derivations. We'll see this case in the example.

Steps

  1. Copy the right-hand side of the assignment operation.
  2. Substitute references to the Temp with this expression.

Remember to run unit tests after each stage. You don't have unit tests? That's a different problem.

This refactoring is really simple, but the devil's in the details; plus I do not want you to slack today, so I have prepared a code sample just as well.

Example

The code sample starts from a simple object that should produce an HTML bit:

<?php
class AccountViewTest extends PHPUnit_Framework_TestCase
{
    /**
     * I know, CSS classes named with colors are a smell. But we're focusing
     * on the PHP side for now.
     * This test checks that AccountView visualizes the amount of money
     * in it as red when appropriate. We'll change this requirement and as
     * a result perform a refactoring.
     */
    public function testIsDisplayedAsRedWhenNegative()
    {
        $account = new AccountView(-20);
        $this->assertEquals('<div class="red">-20</div>', $account->render());
    }
}

class AccountView
{
    private $total;

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

    public function render()
    {
        $negative = $this->total < 0;
        $class =  $negative ? 'red' : 'green';
        return "<div class=\"$class\">$this->total</div>";
    }
}

Then we add a requisite, and since the temporary variables used before are inadequate, the code starts exploding:

<?php
class AccountViewTest extends PHPUnit_Framework_TestCase
{
    /**
     * I know, CSS classes named with colors are a smell. But we're focusing
     * on the PHP side for now.
     * This test checks that AccountView visualizes the amount of money
     * in it as red when appropriate. We'll change this requirement and as
     * a result perform a refactoring.
     */
    public function testIsDisplayedAsRedWhenNegative()
    {
        $account = new AccountView(-20);
        $this->assertEquals('<div class="red">-20</div>', $account->render());
    }
    
    /**
     * We add an allowed overdraft as parameter.
     */
    public function testIsDisplayedAsMixedWhenNotTooMuchNegative()
    {
        $account = new AccountView(-20, 1000);
        $this->assertEquals('<div class="red">-20 (<span class="green">maximum overdraft: 1000</span>)</div>', $account->render());
    }
}

class AccountView
{
    private $total;

    public function __construct($total, $maximumOverdraft = 0)
    {
        $this->total = $total;
        $this->maximumOverdraft = $maximumOverdraft;
    }

    /**
     * This implementation doesn't work: $negative have different meanings in 
     * case of negative total or negative total larger than the maximum overdraft.
     */
    public function render()
    {
        $negative = $this->total < 0;
        $class =  $negative ? 'red' : 'green';
        if ($negative && $this->maximumOverdraft) {
            $overdraftNotice =" (<span class=\"$class\">maximum overdraft: $this->maximumOverdraft</span>)";
        } else {
            $overdraftNotice = '';
        }
        return "<div class=\"$class\">{$this->total}{$overdraftNotice}</div>";
    }
}

Getting rid of the temporary variables makes the logic more clear, and paves the way for the extraction of the conditions in dedicated methods. With the former level of indirection, extracting a method directly would require passing the booleans as parameters, or some cowboy-coding where we cut and paste around lines until the tests pass again.

<?php
class AccountViewTest extends PHPUnit_Framework_TestCase
{
   // ...
}

class AccountView
{
    private $total;

    public function __construct($total, $maximumOverdraft = 0)
    {
        $this->total = $total;
        $this->maximumOverdraft = $maximumOverdraft;
    }

    /**
     * We get rid of $negative and inline. Since the code now sucks, we're ready
     * for other refactorings that weren't possible while referring to
     * $negative: it contained too few information for us to display the right
     * colors.
     */
    public function render()
    {
        $class =  $this->total < 0 ? 'red' : 'green';
        if ($this->maximumOverdraft && $this->total < 0) {
            $overDraftClass = 'green'; // this will become a computed value after we add more tests
            $overdraftNotice =" (<span class=\"green\">maximum overdraft: $this->maximumOverdraft</span>)";
        } else {
            $overdraftNotice = '';
        }
        return "<div class=\"$class\">{$this->total}{$overdraftNotice}</div>";
    }
} 

Discover how TDM Is Essential To Achieving Quality At Speed For Agile, DevOps, And Continuous Delivery. Brought to you in partnership with CA Technologies

Topics:

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}