Adapter Pattern,PHP 適配器模式

white apple adapter with box

簡介

什麼是 Adapter Pattern

Adapter Pattern 是一種結構型設計模式,用於讓兩個不相容的介面能夠協同工作。通過創建一個額外的類別,將一個介面轉換為另一個客戶端期望的介面。

什麼狀況適合使用 Adapter Pattern

  1. 系統需要使用現有的類別,但其介面不符合系統需求。
  2. 要整合多個不同的庫或 API,它們有不同的介面或資料格式。
  3. 需要重用一些遺留代碼,但不希望修改原有的介面或代碼。

範例教學

我們以一個咖啡機為例。假設我們有一個舊式咖啡機,它使用的介面和我們新系統不相容。所以使用 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

  1. Facade Pattern: 隱藏系統的複雜性,而提供一個統一的介面。
  2. Bridge Pattern: 分離一個物件的抽象和實現,使兩者可以獨立地變化。

主要的差別在於 Adapter 是將一個介面轉換成另一個,而 Facade 和 Bridge 則著重於設計更為靈活、可擴展的架構。

參考來源

  1. “Design Patterns: Elements of Reusable Object-Oriented Software” by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides
  2. PHP: The Right Way

發佈留言

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