C H A P T E R  7

image

Structuring Behavior

A big problem in taming the beast of old legacy code often arises from a method's length. Methods can be too long, too short, overused, or underused. Long methods are hard to keep in mind at once with all their meaning, while short ones can be not meaningful enough. Misused methods can compromise a clean structure by adding unwanted complexity.

In both cases we have to refactor our code to improve its readability, its logical structure, and to keep undesired complexity out of our way. Most of the refactoring techniques we'll see in this book are based on the collection of refactoring methods published by Martin Fowler [FOW01]. For each method we'll see the reason for it, typical use scenarios, refactoring mechanics, and some real-world examples to see how each method works. Here we will learn how to move code back and forth from a stand-alone method and inline code, as well as how to use or get rid of temporary variables to obtain a clear and reusable code. The refactoring techniques in this chapter are among the ones you'll use most frequently, since they also often represent a middle step toward other refactoring.

Extract Method

Problem: “You have a portion of code you can group together.”

Solution: “Turn that code into a method named to explain the purpose of the method.”

Motivation

The “Extract Method” is a common refactoring meant to communicate meaning to the reader. If you have an excerpt of code that is too long to be clear or you feel the temptation to add comments to explain its purpose, this is the refactoring technique that will help you.

According to Martin Fowler [FOW01], short and well-named methods are preferable, for three reasons at least. First, small methods implicitly carry a smaller behavior, which is likely to be more focused on a given task. These fine-grained methods are easier to use across the whole application. Second, as higher-level methods composed by smaller ones, they are more readable, reducing the need for comments. Third, small methods can be overridden more easily, since they expose a more task-focused interface with fewer side effects.

Fowler also gives us a good example of pragmatic attitude, explaining what the right method length should be: if clarity gets improved, extract the code, even when the method name is longer than the method body.

Mechanics

  • Create a new method, and name it after what it does, not how it does it. If you don't manage to find such an expressive name, don't extract the code.
  • Copy the extracted code from the source method into the new method.
  • Look for any variables that are local in the scope of the source method. These must be turned into local variables and parameters in the new method scope.
  • Declare as temporary variables any temporary variables used only within the extracted code.
  • Spot any local-scope variables modified by the extracted code. If one variable is modified, try to treat the extracted code as a query and return the result to the external variable to be modified. If there is more than one variable like this you can still try to treat the extracted code as a query using a few workarounds made possible by PHP language constructs, like returning an array and using the list() function to redistribute the values among old-scope variables. You may also need to use other refactoring techniques like “Split Temporary Variable” or “Replace Temp With Query,” which you'll learn about later in this chapter.
  • Pass local scope variables that are read from extracted code into the new method as parameters.
  • Replace the extracted code in the source method with a call to the target method.
  • Remove any declaration of temporary variables moved inside the target method.
  • Run tests.

Example: No Local Variables

The extract method can be really easy. Consider the following class method:

class Customer
{
  public $orders = array();
  public $name;

  public function printOwing()
  {
    echo '<h2>Customer owes</h2>';
    echo '<h3>Amount based on orders updated yesterday</h3>';

    // calculate oustanding
    $outstanding = 0;
    foreach ($this->orders as $order)
    {
      $outstanding += $order['amount'];
    }

    // print details
    echo "<div>Name: {$this->name}</div>";
    echo "<div>Amount: $outstanding</div>";
  }
}

Customer class is tested by the following unit test:

class CustomerTest extends PHPUnit_Framework_TestCase
{
  public function testPrintOwing()
  {
    $customer = new Customer();
    $customer->name = 'Edmundo';
    $customer->orders = array(
      array('amount' => 110),
      array('amount' => 300),
      array('amount' => 150)
    );

    ob_start();
    $customer->printOwing();
    $output = ob_get_contents();
    ob_end_clean();

    $this->assertRegExp('/<h2>Customer owes</h2><h3>Amount based on orders updated image
yesterday</h3>/', $output);
    $this->assertRegExp('/Name: Edmundo/', $output);
    $this->assertRegExp('/Amount: $560/', $output);
  }
}

The code printing the header is very easy to extract, just a matter of cutting and pasting:

class Customer
{
  public $orders = array();
  public $name;

  public function printOwing()
  {
    $this->printHeader();

    // calculate oustanding
    $outstanding = 0;
    foreach ($this->orders as $order)
    {
      $outstanding += $order['amount'];
    }

    // print details
    echo "<div>Name: {$this->name}</div>";
    echo "<div>Amount: $outstanding</div>";
  }

  public function printHeader()
  {
    echo '<h2>Customer owes</h2>';
    echo '<h3>Amount based on orders updated yesterday</h3>';
  }
}

Example: Using Local Variables

We saw an example that's too easy. Local variables are there more often than not, and scope issues require extra work to be correctly managed. In some cases they can even prevent us from doing refactoring at all.

Let's see an easy one: when local variables are read-only we can just pass them as parameters to a newly-written method. In our example we can make this step with the new printDetails($outstanding) method:

class Customer
{
  public $orders = array();
  public $name;

  public function printOwing()
  {
    $this->printHeader();

    // calculate oustanding
    $outstanding = 0;
    foreach ($this->orders as $order)
    {
      $outstanding += $order['amount'];
    }

    $this->printDetails($outstanding);
  }

  public function printDetails($outstanding)
  {
    echo "<div>Name: {$this->name}</div>";
    echo "<div>Amount: $$outstanding</div>";
  }

  public function printHeader()
  {
    echo '<h2>Customer owes</h2>';
    echo '<h3>Amount based on orders updated yesterday</h3>';
  }
}

It also works if the local variable is an object and you have to invoke a modifying method: you just pass the object in as a parameter.

Example: Reassigning a Local Variable

In the previous example, if we want to extract the calculation we have to cope with the return value of the calculation itself. Let's have a look at the code:

public function printOwing()
{
  $this->printHeader();

  // calculate outstanding
$outstanding = 0;
  foreach ($this->orders as $order)
  {
    $outstanding += $order['amount'];
  }

  $this->printDetails($outstanding);
}

As we may see, $outstanding is calculated in the original method but passed as a parameter to the printDetails() method. If we move the calculation of $outstanding into a target method by the extract method, we'll have to store the calculation result in some temporary variable in the source method scope.

class Customer
{
  public $orders = array();
  public $name;

  public function printOwing()
  {
    $this->printHeader();

    $outstanding = $this->getOutstanding();

    $this->printDetails($outstanding);
  }

  public function getOutstanding()
  {
    $outstanding = 0;
    foreach ($this->orders as $order)
    {
      $outstanding += $order['amount'];
    }

    return $outstanding;
  }
  [...]
}

We may notice how the $outstanding variable is now initialized within the target method. This is possible only since the variable has a fixed initialization value. In the next example we will learn how to cope with non-static initializations. The code here shows the updated test.

class CustomerTest extends PHPUnit_Framework_TestCase
{
  public function testPrintOwing()
  {
    $customer = new Customer();
    $customer->name = 'Edmundo';
    $customer->orders = array(
      array('amount' => 110),
      array('amount' => 300),
      array('amount' => 150)
    );
ob_start();
    $customer->printOwing();
    $output = ob_get_contents();
    ob_end_clean();

    $this->assertRegExp('/<h2>Customer owes</h2><h3>Amount based on orders updated image
yesterday</h3>/', $output);
    $this->assertRegExp('/Name: Edmundo/', $output);
    $this->assertRegExp('/Amount: $560/', $output);

    ob_start();
    $customer->printOwing(110);
    $output = ob_get_contents();
    ob_end_clean();

    $this->assertRegExp('/Amount: $670/', $output);
  }
}

This is followed by the refactored class methods.

[...]
public function printOwing($previous_outstanding = 0)
{
  $this->printHeader();

  $outstanding = $this->getOutstanding($previous_outstanding);

  $this->printDetails($outstanding);
}

public function getOutstanding($previous_outstanding)
{
  $outstanding = $previous_outstanding;
  foreach ($this->orders as $order)
  {
    $outstanding += $order['amount'];
  }

  return $outstanding;
}
[...]

What about many temporary variables? How can we cope with the need to assign many local variables? In PHP we can return an array and then distribute it with the list() construct.

Let's assume we have to retrieve more details about our customer: outstanding owed amount and the number of critical orders over $120. These are the new tests.

class CustomerTest extends PHPUnit_Framework_TestCase
{
  public function testPrintOwing()
  {
    $customer = new Customer();
    $customer->name = 'Edmundo';
    $customer->orders = array(
array('amount' => 110),
      array('amount' => 300),
      array('amount' => 150)
    );

    ob_start();
    $customer->printOwing();
    $output = ob_get_contents();
    ob_end_clean();

    $this->assertRegExp('/<h2>Customer owes</h2><h3>Amount based on orders updated image
yesterday</h3>/', $output);
    $this->assertRegExp('/Name: Edmundo/', $output);
    $this->assertRegExp('/Amount: $560/', $output);
    $this->assertRegExp('/Critical active orders: 2/', $output);

    ob_start();
    $customer->printOwing(110);
    $output = ob_get_contents();
    ob_end_clean();

    $this->assertRegExp('/Amount: $670/', $output);
    $this->assertRegExp('/Critical active orders: 2/', $output);
  }
}

In the class we have to pass two parameters to the printDetails() method, and both are calculated in the same foreach() loop.

class Customer
{
  public $orders = array();
  public $name;

  public function printOwing($previous_outstanding = 0)
  {
    $this->printHeader();

    list($outstanding, $critical) = $this->getOutstanding($previous_outstanding);

    $this->printDetails($outstanding, $critical);
  }

  public function getOutstanding($previous_outstanding)
  {
    $outstanding = $previous_outstanding;
    $critical = 0;
    foreach ($this->orders as $order)
    {
      $outstanding += $order['amount'];
      if ($order['amount'] > 120)
      {
        $critical++;
      }
    }
return array($outstanding, $critical);
}

  public function printDetails($outstanding, $critical)
  {
    echo "<div>Name: {$this->name}</div>";
    echo "<div>Amount: $outstanding</div>";
    echo "<div>Critical active orders: $critical</div>";
  }

  public function printHeader()
  {
    echo '<h2>Customer owes</h2>';
    echo '<h3>Amount based on orders updated yesterday</h3>';
  }
}

Though we just provided a solution, we prefer to use single return values as much as possible. To honor this preference and, when impossible to make a clean extraction at all, we can reduce the presence of temps by using “Replace Temp with Query” or choosing to work around the issue by means of other refactoring techniques like “Replace Method with Method Object.”

Inline Method

Problem: “Code in a method is no more than its name.”

Solution: “Move the called code into the body of the caller and remove the called method.”

Motivation

Since indirection is good except when not needed, it is sometimes useful to get rid of a method in case it's short to the point of being as simple and clear as its name. This can happen after a previous refactoring of the method body, bringing you to a clearer and shorter version of it.

Another possible use of this refactoring technique is as a middle step towards other bigger refactoring, to bring the whole involved behavior into a single point and then refactor it again.

Mechanics

  • Check that the method is not overridden by any subclasses.
  • Find all calls to the method.
  • Replace calls with the method body.
  • Run tests.
  • Remove the definition.

Example

We have a very small class Person and its unit test.

class Person
{
  public $firstname;
  public $lastname;

  public function getCompleteName()
  {
    return $this->getFirstName().' '.$this->getLastName();
  }

  public function getFirstName()
  {
    return $this->firstname;
  }

  public function getLastName()
  {
    return $this->lastname;
  }
}
class PersonTest extends PHPUnit_Framework_TestCase
{
  public function testGetCompleteName()
  {
    $person = new Person();
    $person->firstname = 'Edmundo';
    $person->lastname = 'Caipirinha';

    $this->assertEquals('Edmundo Caipirinha', $person->getCompleteName());
  }
}

Doing this refactoring is as easy as writing the following code.

class Person
{
  public $firstname;
  public $lastname;

  public function getCompleteName()
  {
    return $this->firstname.' '.$this->lastname;
  }
}

You may have noted already that we performed the same refactoring twice in this case, one for the firstname property and one for the lastname.

Inline Temp

Problem: “A simple expression is assigned once to a temporary variable now in the way of other refactorings.”

Solution: “Replace references to temporary variables with the simple expression.”

Motivation

If a temp is assigned the value of a method call you can decide to inline it. Usually you should inline it if it's preventing you from performing other refactoring techniques like “Extract Method” or as part of “Replace Temp with Query.”

Mechanics

  • Find all references to the temp.
  • Check that the temp is assigned only once.
  • Replace each reference to the temp with the right-hand side expression.
  • Test after each replace.
  • Remove the temp declaration, if any, and the temp assignment.
  • Run tests.

Example

We have a simple Chater class and its unit test.

class Charter
{
  public $travellers;

  public function isOverbooked()
  {
    $overbooked = sizeof($this->travellers) > 120;
    return $overbooked;
  }
}

class CharterTest extends PHPUnit_Framework_TestCase
{
  public function testIsOverbooked()
  {
    $charter = new Charter();
    $charter->travellers = range(0, 125);

    $this->assertTrue($charter->isOverbooked());
  }
}

We want to apply “Inline Temp” in isOverbooked() method. To do this, we remove $overbooked variable and directly return the condition value.

class Charter
{
  public $travellers;

  public function isOverbooked()
  {
    return sizeof($this->travellers) > 120;
  }
}

Replace Temp with Query

Problem: “You are using a temporary variable to store and hold an expression result.”

Solution: “Extract a new method and replace all references to the temp with a method call.”

Motivation

Local variables encourage a procedural coding style, since they are accessible only in the scope of the method in which they are used. By replacing the temp with a query method, you can make the same information available from any other method in the class. That will strongly contribute to cleaner code and a better structure.

“Replace Temp with Query” is often used before “Extract Method,” and its stand-alone use is possible only when temps are assigned once and when the assigned right-hand side expression is side-effect free.

Mechanics

  • Look for a temp that is assigned once.
  • Extract the right-hand side of the assignment to a private method.
  • Run tests.
  • Perform “Inline Temp” on the temp.
  • Run tests.

Obviously the private access modifier is initially suitable for the method. As soon as we find another use for the method we can easily weaken its protection.

If the temp to be replaced is used in a loop as an accumulator, the entire loop can be extracted into a method, making things a lot cleaner. If more than one temp is used in the loop, extract it to a method as many times as needed so that you can replace each temp with a query. As Martin Fowler says, any concern about performance should be let slide at the moment of refactoring. At optimization time, the better structure you'll have got by means of refactoring will make you able to optimize wherever needed.

Example

We first consider the following Product class:

class Product
{
  private $price = 5;
  public $quantity;

  public function getPrice()
  {
    $base_price = $this->price * $this->quantity;

    $discount = ($base_price > 1000) ? .90 : 1;

    return $base_price * $discount;
  }
}

The ProductTest PHPUnit class tests all product discount rules.

class ProductTest extends PHPUnit_Framework_TestCase
{
  public function testGetPrice()
  {
    $product = new Product();

    $product->quantity = 200;
    $this->assertEquals(1000, $product->getPrice());

    $product->quantity = 300;
    $this->assertEquals(1350, $product->getPrice());
  }
}

Our aim is to replace both $base_price and $discount temps. Let's move step by step—one temp first, then the second one.

The $base_price temp is assigned only once, so we can move forward to the extraction of the right-hand expression in a new private method.

public function getPrice()
  {
    $base_price = $this->getBasePrice();

    $discount = ($base_price > 1000) ? .90 : 1;

    return $base_price * $discount;
  }

  private function getBasePrice()
  {
    return $this->price * $this->quantity;
  }

Tests are still green, so I can perform “Inline Temp.”

public function getPrice()
  {
    $discount = ($this->getBasePrice() > 1000) ? .90 : 1;

    return $this->getBasePrice() * $discount;
  }

  private function getBasePrice()
  {
    return $this->price * $this->quantity;
  }

Tests are steady on green, with one temp gone. Good! Now let's replace $discount temp in a similar way. First, do the method extraction:

public function getPrice()
  {
    $discount = $this->getDiscount();

    return $this->getBasePrice() * $discount;
  }

  private function getDiscount()
  {
    return ($this->getBasePrice() > 1000) ? .90 : 1;
  }

Then, after a brief test run to ensure everything's right, perform “Inline Temp” to get the refactoring complete.

class Product
{
  private $price = 5;
  public $quantity;

  public function getPrice()
  {
    return $this->getBasePrice() * $this->getDiscount();
  }

  private function getDiscount()
  {
    return ($this->getBasePrice() > 1000) ? .90 : 1;
  }

  private function getBasePrice()
  {
    return $this->price * $this->quantity;
  }
}

Please note how hard it would have been to extract getDiscount() if not replacing $base_price with a query. The $base_price temp couldn't exist in both getPrice() and getDiscount() scopes, leading to bad code duplication.

Introduce Explaining Variable

Problem: “You have a complicated expression.”

Solution: “Put the expression or part of it in a temporary variable with a clear name.”

Motivation

“Introduce Explaining Variable” is a very common refactoring technique used to overcome the natural tendency of expressions to become hard to read and understand. When one expression gets too complex, temporary variables are useful to break down the expression into easier chunks.

Mechanics

  • Declare a temporary variable.
  • Assign the result of part of the expression to it.
  • Run tests.
  • Re-iterate over other parts of the expression.

Example

We have a simple Product class, where the only behavior is to calculate its price.

class Product
{
  private $price = 5;
  public $quantity;

  /**
   * Returns total price applying 10% discount
   * on each item over 150th
   */
  public function getPrice()
  {
    return $this->price * $this->quantity -
      max($this->quantity - 150, 0) * $this->price * 0.1;
  }
}

We need to add a comment to the getPrice() method to make it understandable by another reader. While comments are a good means to clarify what a method is supposed to do, it's always better to write self-explaining code as far as it makes sense. Introducing a couple of temps can help a lot to get a more readable code. First we check our tests are green:

class ProductTest extends PHPUnit_Framework_TestCase
{
  public function testGetPrice()
  {
    $product = new Product();

    $product->quantity = 200;
    $this->assertEquals(975, $product->getPrice());
  }
}

Then we move to refactoring the first part of the expression:

public function getPrice()
{
  $base_price = $this->price * $this->quantity;

  return $base_price -
    max($this->quantity - 150, 0) * $this->price * 0.1;
}

After confirming that our test is green, we complete our refactoring with this:

public function getPrice()
{
  $base_price = $this->price * $this->quantity;
  $discount = max($this->quantity - 150, 0) * $this->price * 0.1;

  return $base_price - $discount;
}

This final getPrice() method lets tests complete with a perfectly green bar.

Split Temporary Variable

Problem: “A temporary variable is assigned more than once.”

Solution: “Declare and assign a separate temp for each assignment.”

Motivation

Except for loop and collecting variables, each temporary variable should be assigned once. A temp being assigned more than once is a clue of a violated single responsibility principle. While we could be easily convinced already of this principle's validity at a higher level—one class, one responsibility—we should follow this principle at a variable level too, as long as it makes sense. Using a temp for two different things is very confusing for the reader, and any variable with more than one single responsibility should be split into as many temps as the responsibilities it holds.

Mechanics

  • Check that the temp is not being used as a collecting variable (e.g., $i = $i + someMethod()).
  • Change the name of a temp at its declaration, if any, and its first assignment.
  • Change all references of the temp up to its second assignment.
  • Declare the temp at its second assignment, as a good practice.
  • Run tests.
  • Repeat for each following assignment.

Example

In the Employee class we write the method named getBalance() to compute the net difference between incomes and expenses, after applying small modifiers to each transaction as a bonus or a penalty. Here is the test:

class EmployeeTest extends PHPUnit_Framework_TestCase
{
  public function testGetBalance()
  {
    $employee = new Employee();
    $employee->payments = array(100, 100, 100, 100);
    $employee->expenses = array(50, 50, 50, 50);

    $this->assertEquals(200.00, $employee->getBalance());
  }
}

Here is the class code:

class Employee
{
  public $payments = array();
  public $expenses = array();

  public function getBalance()
  {
    $balance = 0;

    $modifier = 1.1;

    foreach ($this->payments as $payment)
    {
      $balance += $payment * $modifier;
    }

    $modifier = 1.2;

    foreach ($this->expenses as $expense)
    {
$balance -= $expense * $modifier;
}

    return round($balance, 2);
  }
}

We can see the temp variable $balance being assigned more than once here. It's OK since its purpose is to accumulate each transaction value to contain the balance value at the end of the method and to be returned.

Instead, the $modifier temp is quite obscure. Easy as it may be, would you have gotten at a very first glance what those modifiers mean if they were not introduced to you as bonuses and penalties? We can rename them to get a better method structure. Applying proposed mechanics, at the first round, we get this getBalance() method:

public function getBalance()
{
  $balance = 0;

  $bonus = 1.1;

  foreach ($this->payments as $payment)
  {
    $balance += $payment * $bonus;
  }

  $modifier = 1.2;

  foreach ($this->expenses as $expense)
  {
    $balance -= $expense * $modifier;
  }

  return round($balance, 2);
}

While performing the second round we end with this:

public function getBalance()
{
  $balance = 0;

  $bonus = 1.1;

  foreach ($this->payments as $payment)
  {
    $balance += $payment * $bonus;
  }

  $penalty = 1.2;

  foreach ($this->expenses as $expense)
  {
    $balance -= $expense * $penalty;
  }
return round($balance, 2);
}

This keeps our tests strictly green.

Replace Method with Method Object

Problem: “You have a long method and you can't apply ‘Extract Method’.”

Solution: “Create a new class, instantiate an object, and create a method. Then refactor it.”

Motivation

Sometimes local temps make method decomposition too hard, forcing us to keep very long methods. Since we all love writing good short methods (don't we?), we can perform “Replace Method with Method Object” and turn all those local temps into fields on the method object, and then refactor this new object to decompose the original behavior.

Mechanics

  • Create a new class named as the method.
  • Give the new class an attribute for the original object and for each temp or parameter in the source method.
  • Write a constructor to accept source object and parameters.
  • Create a compute() method in the new class.
  • Copy the body of the old method into the new one, using the object field in every invocation of original object methods and adding $this-> prefix in temps and parameters references.
  • Replace the old method with the object instance creation and a compute() method call.
  • Decompose compute() as much as needed considering that all temps are now object fields.

Example

Consider this not-so-meaningful class:

class Account
{
  private $state = 3;

  public function getTripleState()
  {
    return $this->state * 3;
  }
public function foo($first_param, $second_param)
  {
    $temporary_1 = $first_param * $second_param;
    $temporary_2 = $first_param * $this->getTripleState();

    if (($temporary_1 - $temporary2) % 2)
    {
      return $first_param - 2;
    }

    return $second_param + 4;
  }
}

Consider also this associated test:

class AccountTest extends PHPUnit_Framework_TestCase
{
  public function testFoo()
  {
    $employee = new Account();

    $this->assertEquals(24, $employee->foo(10, 20));
    $this->assertEquals(9, $employee->foo(11, 17));
  }
}

We apply the first steps of “Replace Method with Method Object,” and we get the following class:

class foo
{
  private
    $account,
    $temporary_1,
    $temporary_2,
    $first_param,
    $second_param;

  public function __construct($account, $first_param, $second_param)
  {
    $this->account = $account;
    $this->first_param = $first_param;
    $this->second_param = $second_param;
  }
}

We can now move the method from the source object to the new one.

class foo
{
  [...]
  public function compute()
  {
    $this->temporary_1 = $this->first_param * $this->second_param; image
$this->temporary_2 = $this->first_param * $this->account->getTripleState();
if (($this->temporary_1 - $this->temporary2) % 2)
    {
      return $this->first_param - 2;
    }

    return $this->second_param + 4;
  }
}

In the source class we just replace the old method body like this:

public function foo($first_param, $second_param)
{
  $foo = new foo($this, $first_param, $second_param);
  return $foo->compute();
}

That's all. The best has yet to come, though, because at this point we can start extracting methods without bothering about arguments, like in the example here:

public function compute()
{
  $this->setup();

  if (($this->temporary_1 - $this->temporary2) % 2)
  {
    return $this->first_param - 2;
  }

  return $this->second_param + 4;
}

public function setup()
{
  $this->temporary_1 = $this->first_param * $this->second_param;
  $this->temporary_2 = $this->first_param * $this->account->getTripleState();
}

Substitute Algorithm

Problem: “You want to replace an algorithm with another.”

Solution: “Replace the body of the method with the new algorithm.”

Motivation

Sometimes the only way to make things simpler is to replace the code you have with another one. This may happen when you gain deeper insight into the business domain or when a newly-introduced library does what you already coded to do.

Sometimes when you want to change code to alter behavior just a little bit, it's better to substitute the algorithm first to have a simpler base to start from.

“Substitute Algorithm” is a radical refactoring you should perform after decomposing your methods as much as possible, to keep the problem down to a tractable size.

Mechanics

  • Prepare your alternative algorithm.
  • Run tests.
  • Use both old and new algorithms to run tests and ease debugging.

Example

A very simple example shows the importance of tests in refactoring. We have the BalanceTest unit test.

class BalanceTest extends PHPUnit_Framework_TestCase
{
  public function testGetAmount()
  {
    $balance = new Balance();
    $balance->transactions = array(50, −20, 10, 500);

    $this->assertEquals(540, $balance->getAmount());
  }
}

The Balance class satisfy the previous test, as long as we get a green test result.

class Balance
{
  public $transactions = array();

  public function getAmount()
  {
    $amount = 0;

    foreach ($this->transactions as $transaction)
    {
      $amount += $transaction;
    }

    return $amount;
  }
}

If we “Substitute Algorithm”, we can run our tests, and we see that our substitution worked.

public function getAmount()
{
  return array_sum($this->transactions);
}

Summary

The most common refactoring techniques in our day-to-day activities are about composing methods to make the right bricks for our code-building. Problems often come from methods that are too long to be used in a well-structured architecture, since they convey way too much behavior and hold too much information. The most important refactoring technique we saw in this chapter is “Extract Method,” since it directly faces that complexity with a divide et impera strategy. By the way, all the other refactoring techniques shown in this chapter are very useful to package your code in a meaningful way, and they will prove their usefulness often.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.116.126