1. 程式人生 > >深入PHP面向物件、模式與實踐——模式原則(1)

深入PHP面向物件、模式與實踐——模式原則(1)

組合

  • 模式的啟示

通過以靈活的方式來組合物件,元件能在執行時被定義。《設計模式》將此提煉出一個原則:組合優於繼承。

  • 組合與繼承

繼承是應對變化的環境及上下文設計的有效方式,然而它會限制靈活性,尤其是當類承擔了過多的責任的時候。

  • 問題
    以下圖為例:
    這裡寫圖片描述

利用這種繼承模式,我們可以在課程的實現之間切換。可是如果引入一組新的特殊性,又會怎麼樣?比如我們需要處理演講和研討會。使用繼承如下:
這裡寫圖片描述

在該體系中,我們不得不大量重複開發功能,否則無法使用繼承樹來管理價格機制。我們可能考慮在父類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()。這種顯示呼叫另一個物件的方法來執行一個請求的方式便是“委託”。

如你所見,此結構的效果之一便是讓我們關注類的職責。組合使用物件比繼承體系更靈活,因為組合可以以多種方式動態地處理任務,不過這可能導致程式碼的可讀性下降。因為組合需要更多的物件型別,而這些型別的關係並不像在繼承關係中那邊有固定的可預見性。