首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 設計模式:裝飾器模式完整實作與應用場景

     編輯:PHP 綜合知識

裝飾器模式:動態擴展物件功能的優雅方案

裝飾器模式屬於結構型設計模式,允許在不改變原有類別、不使用繼承的情況下,動態地為物件添加新行為。就像「大口袋套小口袋」,層層包裹核心物件。

核心概念

  • Component (抽象元件):定義核心介面
  • ConcreteComponent (具體元件):核心物件 (被裝飾者)
  • Decorator (裝飾者基底):持有 Component 參考、實作相同介面
  • ConcreteDecorator (具體裝飾者):添加具體功能

經典範例:午餐點餐系統

<?php
// 1. 抽象元件介面
interface Food {
    public function getDescription(): string;
    public function getCost(): float;
}

// 2. 具體元件:基礎飯
class Rice implements Food {
    public function getDescription(): string { return "白飯"; }
    public function getCost(): float { return 20.0; }
}

// 3. 裝飾者基底:抽象菜色
abstract class SideDish implements Food {
    protected Food $food; // 持有被裝飾物件
    
    public function __construct(Food $food) {
        $this->food = $food;
    }
    
    // 預設轉發給被裝飾物件
    public function getDescription(): string {
        return $this->food->getDescription();
    }
    public function getCost(): float {
        return $this->food->getCost();
    }
}

// 4. 具體裝飾者:各種菜色
class BraisedPork extends SideDish {
    public function getDescription(): string {
        return $this->food->getDescription() . " + 滷肉";
    }
    public function getCost(): float {
        return $this->food->getCost() + 30.0;
    }
}

class TeaEgg extends SideDish {
    public function getDescription(): string {
        return $this->food->getDescription() . " + 茶葉蛋";
    }
    public function getCost(): float {
        return $this->food->getCost() + 10.0;
    }
}

class Pickles extends SideDish {
    public function getDescription(): string {
        return $this->food->getDescription() . " + 醃菜";
    }
    public function getCost(): float {
        return $this->food->getCost() + 5.0;
    }
}

// 5. 客戶端使用:動態組合
$lunch = new Rice();                    // 基礎:白飯 $20
$lunch = new BraisedPork($lunch);       // 加滷肉  $30
$lunch = new TeaEgg($lunch);            // 加茶蛋  $10
$lunch = new Pickles($lunch);           // 加醃菜  $5

echo "餐點: " . $lunch->getDescription() . "\n"; // 白飯 + 滷肉 + 茶葉蛋 + 醃菜
echo "總價: $" . $lunch->getCost() . "\n";       // $65
?>

UML 類別圖 (文字版)

+----------------+       +------------------+
|   <>|       |   Food           |
|    Food        |<|-----|   (Decorator)    |
|----------------|       |------------------|
| +getDescription|       | -food: Food      |
| +getCost()     |       |------------------|
+----------------+       | +__construct()   |
        ^                | +getDescription()|
        |                | +getCost()       |
        |                +------------------+
        |                        ^
        |                        |
+-------+--------+     +---------+----------+
|   Rice         |     | BraisedPork        |
|----------------|     |------------------- |
| +getDescription|     | +getDescription()  |
| +getCost()     |     | +getCost()         |
+----------------+     +--------------------+
        ^                +---------+----------+
        |                          |
        |                   +--------+----------+
        |                   | TeaEgg             |
        |                   |------------------- |
        |                   | +getDescription()  |
        |                   | +getCost()         |
        |                   +--------------------+
        |                   +---------+----------+
        |                             |
        |                      +--------+----------+
        |                      | Pickles           |
        |                      |------------------- |
        |                      | +getDescription()  |
        |                      | +getCost()         |
        |                      +--------------------+

裝飾器 vs 繼承 比較

維度 繼承 裝飾器模式
靈活性 靜態、編譯時決定 動態、執行時組合
類別爆炸 易產生大量子類別 (RiceWithPork, RiceWithEgg, RiceWithPorkAndEgg...) 避免類別爆炸,組合數 = 排列組合
單一職責 違反 (功能混雜在子類別) 符合 (每個裝飾者單一功能)
開放封閉 修改基底類別才能擴展 新增裝飾者不修改既有代碼
適用場景 「是一種」關係、穩定層級 「有某功能」關係、頻繁變動組合

實戰應用場景

  1. I/O 流處理:new BufferedReader(new FileReader(new GZIPInputStream(new FileInputStream("file.gz")))) (Java 風格)
  2. HTTP 中介軟體:Laravel/PHP-DI 中介軟體鏈 (驗證 → 日誌 → CORS → 核心處理)
  3. 圖片處理管線:$img = new Watermark(new Resize(new Crop(new Image("src.jpg"))))
  4. 快取策略:$cache = new RedisCache(new FileCache(new NullCache($service)))
  5. 表單驗證鏈:$validator = new EmailRule(new RequiredRule(new LengthRule($input)))

PHP 內建支援:IteratorAggregate + 裝飾器

<?php
class LoggingIterator implements IteratorAggregate {
    private $inner;
    public function __construct(Traversable $inner) { $this->inner = $inner; }
    public function getIterator(): Traversable {
        echo "開始迭代\n";
        foreach ($this->inner as $k => $v) {
            echo "存取: $k => $v\n";
            yield $k => $v;
        }
        echo "結束迭代\n";
    }
}

$data = [1,2,3];
$logged = new LoggingIterator(new ArrayIterator($data));
foreach ($logged as $v) { /* ... */ }
?>

注意事項

  • 裝飾者與被裝飾者介面必須一致
  • 過度層疊會增加除錯難度 (除錯堆疊深)
  • 順序敏感:new A(new B($obj)) ≠ new B(new A($obj))
  • 型別檢查:instanceof 只能檢測最外層,需遞迴檢查內層
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved