Template Method Pattern,PHP 模板方法模式

computer screen displaying website home page

簡介

什麼是 Template Method Pattern

Template Method Pattern 是一種行為型設計模式,主要用於封裝一個演算法或一個工作流程的骨架,同時允許子類別在不改變結構的情況下,覆寫或擴充某些步驟。

這種模式特別有用於當多個類別有相似的方法,但其中某些步驟有所不同時。透過這種方式,我們可以將共同的邏輯放在一個父類別中,而專屬的邏輯則放在各自的子類別中。

Template Method Pattern 主要由一個抽象父類別和一個或多個具體子類別組成。父類別定義一個稱為「模板方法」的方法,這個方法會調用一系列步驟(也是方法),這些步驟可以在子類別中被覆寫或擴充。

template pattern class diagram
Template Pattern 類別圖

想像你在製作咖啡和茶,兩者的基本步驟相似(煮水、加入主要成分、攪拌、倒入杯子),但某些細節(例如加糖、加牛奶)可能不同。Template Method Pattern 就像是一個飲料製作的指南,它告訴你基本步驟,但允許你根據不同的飲料來調整某些步驟。

什麼狀況適合使用 Template Method Pattern

  1. 當多個類別有相似的工作流程,但其中某些步驟需要不同實作時。
  2. 當你希望子類別能夠擴充父類別的行為,但不改變其結構。
  3. 當你希望將算法的框架和實際實作分開,以提高可維護性和可擴充性。

範例教學

原始程式 (未使用 Template Method Pattern)

在這個原始範例中,我們將製作兩種飲料:咖啡和茶。每種飲料都有自己的類別,並且有一些重複的方法。

<?php
// Coffee 類別
class Coffee {
    public function prepareCoffee() {
        $this->boilWater();
        $this->brewCoffee();
        $this->pourInCup();
        $this->addSugarAndMilk();
    }

    private function boilWater() {
        echo "Boiling water\n";
    }

    private function brewCoffee() {
        echo "Brewing coffee\n";
    }

    private function pourInCup() {
        echo "Pouring into cup\n";
    }

    private function addSugarAndMilk() {
        echo "Adding sugar and milk\n";
    }
}

// Tea 類別
class Tea {
    public function prepareTea() {
        $this->boilWater();
        $this->steepTeaBag();
        $this->pourInCup();
        $this->addLemon();
    }

    private function boilWater() {
        echo "Boiling water\n";
    }

    private function steepTeaBag() {
        echo "Steeping the tea\n";
    }

    private function pourInCup() {
        echo "Pouring into cup\n";
    }

    private function addLemon() {
        echo "Adding lemon\n";
    }
}

很明顯有以下缺點

  1. 重複的程式碼:boilWater()pourInCup() 在兩個類別中都有。
  2. 難以維護:如果需要改變煮水或倒入杯子的方式,必須在兩個類別中都進行修改。

改善範例 (使用 Template Method Pattern)

使用 Template Method Pattern,可以將共同的邏輯放在一個父類別中。

<?php
// Beverage 父類別
abstract class Beverage {
    public function prepareBeverage() {
        $this->boilWater();
        $this->brew();
        $this->pourInCup();
        $this->addCondiments();
    }

    private function boilWater() {
        echo "Boiling water\n";
    }

    abstract protected function brew();

    private function pourInCup() {
        echo "Pouring into cup\n";
    }

    abstract protected function addCondiments();
}

// Coffee 子類別
class Coffee extends Beverage {
    protected function brew() {
        echo "Brewing coffee\n";
    }

    protected function addCondiments() {
        echo "Adding sugar and milk\n";
    }
}

// Tea 子類別
class Tea extends Beverage {
    protected function brew() {
        echo "Steeping the tea\n";
    }

    protected function addCondiments() {
        echo "Adding lemon\n";
    }
}

這樣設計減少了重複的程式碼,更易於維護和擴充。

UML Class Diagram

更多延伸範例

在這個延伸範例中,我們將添加一個新的飲料類型:熱巧克力。這個新類型也會繼承自 Beverage 父類別。

<?php
// HotChocolate 子類別
class HotChocolate extends Beverage {
    protected function brew() {
        echo "Mixing hot chocolate powder with water\n";
    }

    protected function addCondiments() {
        echo "Adding whipped cream\n";
    }
}

使用這個新的子類別,我們可以輕易地製作熱巧克力,而不需要改變 Beverage 父類別或其他子類別。

<?php
$hotChocolate = new HotChocolate();
$hotChocolate->prepareBeverage();

這會輸出:

Boiling water
Mixing hot chocolate powder with water
Pouring into cup
Adding whipped cream

程式運作說明

  1. HotChocolate 類別繼承了 Beverage 父類別。
  2. 覆寫 brew()addCondiments() 方法,以適應熱巧克力的製作過程。
  3. 當調用 prepareBeverage() 方法時,它會按照定義在 Beverage 父類別中的模板方法來執行。

這個延伸範例展示了 Template Method Pattern 的一個重要優點:它允許我們輕易地添加新的子類別來擴充功能,而不需要修改現有的程式碼。

其他類似的 Design Pattern

Strategy Pattern 和 Template Method Pattern 都用於封裝算法,但主要區別在於 Strategy Pattern 通常用於封裝整個算法,而 Template Method Pattern 則是封裝算法的一部分。Factory Method Pattern 也與 Template Method 有相似之處,但它更注重於創建物件。

參考來源

  1. PHP Design Patterns
  2. Wikipedia: Template method pattern

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *