深入PHP面向物件、模式與實踐——模式原則(1)
阿新 • • 發佈:2018-12-31
組合
- 模式的啟示
通過以靈活的方式來組合物件,元件能在執行時被定義。《設計模式》將此提煉出一個原則:組合優於繼承。
- 組合與繼承
繼承是應對變化的環境及上下文設計的有效方式,然而它會限制靈活性,尤其是當類承擔了過多的責任的時候。
- 問題
以下圖為例:
利用這種繼承模式,我們可以在課程的實現之間切換。可是如果引入一組新的特殊性,又會怎麼樣?比如我們需要處理演講和研討會。使用繼承如下:
在該體系中,我們不得不大量重複開發功能,否則無法使用繼承樹來管理價格機制。我們可能考慮在父類Lesson中使用條件語句來移除那些不適宜的重複。我們是把定價邏輯從繼承樹中一併移除並遷移到父類中,但這與我們通常使用多型代替條件的重構思想背道而馳:
- 使用組合
我們可以使用策略模式來解決這個問題。策略模式適用於將一組演算法移入到一個獨立的型別中。
程式碼描述如下:
abstract class Lesson
{
private $duration;
private $costStrategy;
public function __construct($duration, CostStrategy $strategy)
{
$this->duration = $duration;
$this->costStrategy = $strategy;
}
function cost()
{
return $this->costStrategy->cost($this);
}
function chargeType()
{
return $this->costStrategy->chargeType();
}
function getDuration()
{
return $this->duration;
}
}
class Lecture extends Lesson
{
//
}
class Seminar extends Lesson
{
//
}
abstract class CostStrategy
{
abstract function cost(Lesson $lesson);
abstract function chargeType();
}
class TimedCostStrategy extends CostStrategy
{
function cost(Lesson $lesson)
{
return ($lesson->getDuration() * 5);
}
function chargeType()
{
return "hourly rate"
}
}
class FixedCostStrategy extends CostStrategy
{
function cost(Lesson $lesson)
{
return 30;
}
function chargeType()
{
return "fixed rate";
}
}
$lessons[] = new Seminar(4, new TimedCostStrategy());
$lessons[] = new Lecture(4, new FixedCostStrategy());
foreach ($lessons as $lesson) {
print "lesson charge {$lessons->cost()}.";
print "Charge type: {$lessons->chargeType()}";
}
Lession類需要一個作為屬性的CostStrategy物件。Lession::cost()方法只調用CostStrategy::cost()。這種顯示呼叫另一個物件的方法來執行一個請求的方式便是“委託”。
如你所見,此結構的效果之一便是讓我們關注類的職責。組合使用物件比繼承體系更靈活,因為組合可以以多種方式動態地處理任務,不過這可能導致程式碼的可讀性下降。因為組合需要更多的物件型別,而這些型別的關係並不像在繼承關係中那邊有固定的可預見性。