Table of Contents
簡介
什麼是 Template Method Pattern
Template Method Pattern 是一種行為型設計模式,主要用於封裝一個演算法或一個工作流程的骨架,同時允許子類別在不改變結構的情況下,覆寫或擴充某些步驟。
這種模式特別有用於當多個類別有相似的方法,但其中某些步驟有所不同時。透過這種方式,我們可以將共同的邏輯放在一個父類別中,而專屬的邏輯則放在各自的子類別中。
Template Method Pattern 主要由一個抽象父類別和一個或多個具體子類別組成。父類別定義一個稱為「模板方法」的方法,這個方法會調用一系列步驟(也是方法),這些步驟可以在子類別中被覆寫或擴充。
想像你在製作咖啡和茶,兩者的基本步驟相似(煮水、加入主要成分、攪拌、倒入杯子),但某些細節(例如加糖、加牛奶)可能不同。Template Method Pattern 就像是一個飲料製作的指南,它告訴你基本步驟,但允許你根據不同的飲料來調整某些步驟。
什麼狀況適合使用 Template Method Pattern
- 當多個類別有相似的工作流程,但其中某些步驟需要不同實作時。
- 當你希望子類別能夠擴充父類別的行為,但不改變其結構。
- 當你希望將算法的框架和實際實作分開,以提高可維護性和可擴充性。
範例教學
原始程式 (未使用 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";
}
}
很明顯有以下缺點
- 重複的程式碼:
boilWater()
和pourInCup()
在兩個類別中都有。 - 難以維護:如果需要改變煮水或倒入杯子的方式,必須在兩個類別中都進行修改。
改善範例 (使用 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";
}
}
這樣設計減少了重複的程式碼,更易於維護和擴充。
更多延伸範例
在這個延伸範例中,我們將添加一個新的飲料類型:熱巧克力。這個新類型也會繼承自 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
程式運作說明
HotChocolate
類別繼承了Beverage
父類別。- 覆寫
brew()
和addCondiments()
方法,以適應熱巧克力的製作過程。 - 當調用
prepareBeverage()
方法時,它會按照定義在Beverage
父類別中的模板方法來執行。
這個延伸範例展示了 Template Method Pattern 的一個重要優點:它允許我們輕易地添加新的子類別來擴充功能,而不需要修改現有的程式碼。
其他類似的 Design Pattern
Strategy Pattern 和 Template Method Pattern 都用於封裝算法,但主要區別在於 Strategy Pattern 通常用於封裝整個算法,而 Template Method Pattern 則是封裝算法的一部分。Factory Method Pattern 也與 Template Method 有相似之處,但它更注重於創建物件。