Table of Contents
簡介
什麼是 Adapter Pattern
Adapter Pattern 是一種結構型設計模式,用於讓兩個不相容的介面能夠協同工作。通過創建一個額外的類別,將一個介面轉換為另一個客戶端期望的介面。
什麼狀況適合使用 Adapter Pattern
- 系統需要使用現有的類別,但其介面不符合系統需求。
- 要整合多個不同的庫或 API,它們有不同的介面或資料格式。
- 需要重用一些遺留代碼,但不希望修改原有的介面或代碼。
範例教學
我們以一個咖啡機為例。假設我們有一個舊式咖啡機,它使用的介面和我們新系統不相容。所以使用 Adapter Pattern 來解決這個問題。
原始程式,未使用 Adapter
<?php
// 舊式咖啡機
class OldCoffeeMachine {
public function makeOldStyleCoffee() {
return "Making coffee the old-fashioned way.";
}
}
// 新系統
class CoffeeMaker {
public function brew() {
return "Brewing coffee with the new machine.";
}
}
$oldMachine = new OldCoffeeMachine();
echo $oldMachine->makeOldStyleCoffee(); // 輸出: Making coffee the old-fashioned way.
$newMachine = new CoffeeMaker();
echo $newMachine->brew(); // 輸出: Brewing coffee with the new machine.
?>
這樣的作法有明顯的缺點,無法直接使用 OldCoffeeMachine
在新系統中,因為介面不匹配,需要修改舊有的代碼或寫額外的代碼來整合。
改善範例,使用 Adapter
<?php
// Adapter
class CoffeeMachineAdapter {
private $oldMachine;
public function __construct(OldCoffeeMachine $oldMachine) {
$this->oldMachine = $oldMachine;
}
public function brew() {
return $this->oldMachine->makeOldStyleCoffee();
}
}
$oldMachine = new OldCoffeeMachine();
$adapter = new CoffeeMachineAdapter($oldMachine);
echo $adapter->brew(); // 輸出: Making coffee the old-fashioned way.
?>
這樣的作法能夠無縫地整合舊有系統和新系統,無需修改原有類別,不管新舊系統,都適用一樣的介面 brew().
更多延伸範例
更多類型的咖啡機和對應的適配器,藉以觀察 Adapter 的優勢,與可能的缺陷。
<?php
// 舊式咖啡機
class OldCoffeeMachine {
public function makeOldStyleCoffee() {
return "Making coffee the old-fashioned way.";
}
}
// Espresso 咖啡機
class EspressoMachine {
public function makeEspresso() {
return "Making an espresso.";
}
}
// Mocha 咖啡機
class MochaMachine {
public function makeMocha() {
return "Making a Mocha coffee.";
}
}
// Adapter for OldCoffeeMachine
class OldMachineAdapter {
private $machine;
public function __construct(OldCoffeeMachine $machine) {
$this->machine = $machine;
}
public function brew() {
return $this->machine->makeOldStyleCoffee();
}
}
// Adapter for EspressoMachine
class EspressoMachineAdapter {
private $machine;
public function __construct(EspressoMachine $machine) {
$this->machine = $machine;
}
public function brew() {
return $this->machine->makeEspresso();
}
}
// Adapter for MochaMachine
class MochaMachineAdapter {
private $machine;
public function __construct(MochaMachine $machine) {
$this->machine = $machine;
}
public function brew() {
return $this->machine->makeMocha();
}
}
// 使用
$oldMachine = new OldCoffeeMachine();
$oldMachineAdapter = new OldMachineAdapter($oldMachine);
echo $oldMachineAdapter->brew(); // 輸出: Making coffee the old-fashioned way.
$espressoMachine = new EspressoMachine();
$espressoAdapter = new EspressoMachineAdapter($espressoMachine);
echo $espressoAdapter->brew(); // 輸出: Making an espresso.
$mochaMachine = new MochaMachine();
$mochaAdapter = new MochaMachineAdapter($mochaMachine);
echo $mochaAdapter->brew(); // 輸出: Making a Mocha coffee.
?>
透過使用適配器模式,能夠輕易地為不同類型的咖啡機創建適配器,並使它們能與我們的新系統無縫地整合。
這種方式也讓未來的擴展變得容易。如果有新類型的咖啡機出現,只需要為其創建一個新的適配器即可。這樣,無論咖啡機如何升級或更換,我們的主程式碼都不需要做任何改動。這正是適配器模式的優勢。
其他類似的 Design Pattern
- Facade Pattern: 隱藏系統的複雜性,而提供一個統一的介面。
- Bridge Pattern: 分離一個物件的抽象和實現,使兩者可以獨立地變化。
主要的差別在於 Adapter 是將一個介面轉換成另一個,而 Facade 和 Bridge 則著重於設計更為靈活、可擴展的架構。
參考來源
- “Design Patterns: Elements of Reusable Object-Oriented Software” by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides
- PHP: The Right Way