PHP Abstract
消除 ifs
利用 Abstract 實踐多型
如果你的程式碼太多的 ifs
or switch
的描述,或是四散在各地很多相同的ifs
or switch
程式碼。常常改了一個,忘記改另外一個。其實可以利用 OO Abstract的方式,去實現多型polymorphsim
,也可讓程式碼變得簡潔,容易維護,也容易測試。
這篇筆記主要是來自 Miško Hevery
AngularJS之父,在Google Clean Code Talk的筆記。
何時使用多型
- Object的行為會因為
狀態 State
而不同。 - 需要檢查相同的狀態在很多的地方。
Case1 案例一
讓我們創建第一版的Node來表示這樣的算式:
<?php
namespace DemoAbstract;
class Node
{
public $operator; // string
public $value;
public $leftNode;
public $rightNode;
public function evaluate():float
{
switch ($this->operator) {
case '#':
return $this->value;
case '+':
return $this->leftNode->evaluate() + $this->rightNode->evaluate();
case '*':
return $this->leftNode->evaluate() * $this->rightNode->evaluate();
//case ....
}
}
}
當運算多了一種時,就需要在switch case增加。
觀察 1 + 2 * 3,Node的物件就變成了下圖:
代碼的如下:
$OneNode = new Node();
$OneNode->value = 1.0;
$OneNode->operator = '#';
$TwoNode = new Node();
$TwoNode->value = 2.0;
$TwoNode->operator = '#';
$ThreeNode = new Node();
$ThreeNode->value = 3.0;
$ThreeNode->operator = '#';
$MultiNode = new Node();
$MultiNode->operator = '*';
$MultiNode->leftNode = $TwoNode;
$MultiNode->rightNode = $ThreeNode;
echo $MultiNode->evaluate();
第一版的改進
更進一步分析,其實我們並沒有用到許多Node的屬性:
我們應該可以把Node做些抽離,分成 value node和 operation node。
ValueNode變得很Lightweight。
abstract class BaseNode
{
abstract public function evaluate():float;
}
class ValueNode extends BaseNode
{
public $value;
public function evaluate():float
{
return $this->value;
}
}
下面為 OpNode,可以把數字的情況給移除:
class OpNode extends BaseNode
{
public $operator;
public $leftNode;
public $rightNode;
public function evaluate():float
{
switch ($this->operator) {
case '+':
return $this->leftNode->evaluate() + $this->rightNode->evaluate();
case '*':
return $this->leftNode->evaluate() * $this->rightNode->evaluate();
//case ....
}
}
}
執行的代碼如下:
$OneValueNode = new ValueNode();
$TwoValueNode = new ValueNode();
$ThreeValueNode = new ValueNode();
$OneValueNode->value = 1.0;
$TwoValueNode->value = 2.0;
$ThreeValueNode->value = 3.0;
$MultiNode = new OpNode();
$MultiNode->operator = '*';
$MultiNode->leftNode = $TwoValueNode;
$MultiNode->rightNode = $ThreeValueNode;
echo $MultiNode->evaluate();
第二版的改進
開始針對 OpNode 做些分析:發覺只有Operator的不同:
因此我們應該可以在抽離OpNode如下圖:
因此我們的代碼會變成如下:
abstract class OpNode extends BaseNode
{
public $leftNode;
public $rightNode;
}
class AdditionNode extends OpNode
{
public function evaluate():float
{
return $this->leftNode->evaluate() + $this->rightNode->evaluate();
}
}
class MultiplicationNode extends OpNode
{
public function evaluate():float
{
return $this->leftNode->evaluate() * $this->rightNode->evaluate();
}
}
注意:OpNode變成為abstract class 繼承另一個 abstract class (BaseNode)。PHP程式碼允許這樣的擴充。
另外程式碼變得很簡潔,而且如果要新增加另一個運算時,只要再增加一個型別的OpNode,就可達到擴充性,而且不需要改到其他的運算Node。
相關的程式碼可以從這裡下載。
結論
- 利用多型當新的行為增加時,我們不需要更動到既有的程式碼。
- 每個運算都在不同的檔案或Class,方便我們進行測試和理解。