SoFunction
Updated on 2025-04-14

Introduction to PHP Design Pattern Programming Idioms Page 2/3


Reconstruction

Even the most thoughtful and skilled programmers cannot foresee any subtleties in a software project. Problems always appear unexpectedly, and the requirements may also change, and the result is that the code is optimized, shared and replaced.

Refactoring is an idiomatic method: check all your code and find commonalities or similarities that can be unified and simplified, making your code easier to maintain and expand. Refactoring also involves exploring whether a design pattern can be applied to this specific problem—which can also simplify the solution.

Refactoring, to put it simply, renames a property or method, to put it more complex, compresses an existing class. Changing your code to fit one or more design patterns is another kind of refactoring - you may implement it after reading this book.

No proportionals can better explain and reconstruct!

Let's consider two simple classes: CartLine and Cart. CartLine records the price and quantity of each item in the shopping cart. For example, CartLine may record "Four-See Red Polo Shirts, $19.99 per piece". Cart is a container that is used to load one or more CartLine objects and perform some related calculations, such as the total cost of all items in the shopping cart.

Here are the simple implementations of CartLine and Cart:

// PHP5
class CartLine {
public $price = 0;
public $qty = 0;
}
class Cart {
protected $lines = array();
public function addLine($line) {
$this->lines[] = $line;
}
public function calcTotal() {
$total = 0;
// add totals for each line
foreach($this->lines as $line) {
$total += $line->price * $line->qty;
}
// add sales tax
$total *= 1.07;
return $total;
}
}
The first step of refactoring must have enough tests to cover all your code. This is to ensure that the code you modified cannot produce different results from your original code. By the way, your test code cannot be changed unless you change the requirements (the result your code expects) or find an error in the test instance.

Below is an example of testing CartLine and Cart, which will not change during the reconstruction process.

function TestCart() {
$line1 = new CartLine;
$line1->price = 12; $line1->qty = 2;
$line2 = new CartLine;
$line2->price = 7.5; $line2->qty = 3;
$line3 = new CartLine;
$line3->price = 8.25; $line3->qty = 1;
$cart = new Cart;
$cart->addLine($line1);
$cart->addLine($line2);
$cart->addLine($line3);
$this->assertEqual(
(12*2 + 7.5*3 + 8.25) * 1.07,
$cart->calcTotal());
}
Looking at the code above, you may find that they have some "code smells" (code smell) - weird and seemingly problematic code - they are like candidates for reconstruction. (For more information about code smells, please see /cgi/wiki?codesmell). The two most direct candidates for reconstruction are comments and calculations (calculations related to sales tax, etc.). A form of reconstruction: the Extract Method will extract these ugly code from cart::calcTotal() and replace it with a suitable method, making the code more concise.

For example, you can add two calculation methods: lineTotal() and calcSalesTax():

protected function lineTotal($line) {
return $line->price * $line->qty;
}
protected function calcSalesTax($amount) {
return $amount * 0.07;
}
Now you can override the calcTotal() function:

public function calcTotal() {
$total = 0;
foreach($this->lines as $line) {
$total += $this->lineTotal($line);
}
$total += $this->calcSalesTax($total);
return $total;
}
The changes so far have made sense (at least in the context of this example), and it is helpful for pausing and running the code again to verify that the results are still correct. Remember, a green success bar is displayed! (Translator's note: At the beginning of this chapter, the author mentioned: The green bar means that the tests have passed.)

However, the current code still has some things to be picky about. One of them is to access the public attribute in the new method lineTotal(). It is obvious that the responsibility for calculating the sum of each row should not belong to the Cart class, but should be implemented in the CartLine class.

Refactor again, add a new method total() to CartLine to calculate the long-term price of each item in the order.

public function total() {
return $this->price * $this->qty;
}
Then remove the method lineTotal() from the class Cart and change the calcTotal() method to use the new cartLine::Total() method. Rerun this test and you will still find that the result is a green bar.

This is the newly refactored code:

class CartLine {
public $price = 0;
public $qty = 0;
public function total() {
return $this->price * $this->qty;
}
}
class Cart {
protected $lines = array();
public function addLine($line) {
$this->lines[] = $line;
}
public function calcTotal() {
$total = 0;
foreach($this->lines as $line) {
$total += $line->total();
}
$total += $this->calcSalesTax($total);
return $total;
}
protected function calcSalesTax($amount) {
return $amount * 0.07;
}
}
Now this code no longer needs comments per line, because the code itself better illustrates the function of each line. These new methods better encapsulate the computing function and are easier to adapt to future changes. (For example, consider different large sales tax rates). In addition, these classes are more balanced and easier to maintain.

This example is obviously trivial, but hopefully you can infer from it and imagine how to refactor your own code.

When encoding, you should have one of two patterns: add new features or refactor code. When adding features, you need to write tests and add code. During refactoring, you need to change your original code and make sure that all relevant tests still run correctly.

The main reference materials on reconstruction include Martin Fowler's book "Refactoring: Improving the Design of Existing Code" (Refactoring: Improving the Design of Existing Code). Use some simplified points to summarize Fowler's book, and the steps for reconstruction are as follows:

Define the code that needs to be refactored
There are tests covering all codes
Small steps to work
Run your tests after each step. Coding and testing are quite repetitive - compared with compiled languages, interpreted languages, such as PHP, are much easier.
Use refactoring to make your code more readable and modifiable.
Previous page123Next pageRead the full text