裝飾器模式以組合替代繼承,在不修改原類代碼的前提下,動態為對象添加新行為。它解決「繼承爆炸」問題:若有 3 種武器 × 4 種屬性,繼承需 12 個子類,裝飾器只需 3 + 4 = 7 個類。本文通過「武器+屬性」經典案例,展示標準結構、動態疊加、Laravel 中間件實踐與最佳原則。
<?php
// 1. 抽象組件介面
interface Weapon {
public function getDescription(): string;
public function getCost(): float;
}
// 2. 具體組件:基礎武器
class Glave implements Weapon {
public function getDescription(): string { return '長劍'; }
public function getCost(): float { return 100.0; }
}
class Knife implements Weapon {
public function getDescription(): string { return '匕首'; }
public function getCost(): float { return 80.0; }
}
class Axe implements Weapon {
public function getDescription(): string { return '斧頭'; }
public function getCost(): float { return 200.0; }
}
// 3. 抽象裝飾器:持有組件引用,轉發調用
abstract class WeaponDecorator implements Weapon {
protected Weapon $weapon;
public function __construct(Weapon $weapon) {
$this->weapon = $weapon;
}
// 默認轉發(具體裝飾器可選擇性覆蓋)
public function getDescription(): string { return $this->weapon->getDescription(); }
public function getCost(): float { return $this->weapon->getCost(); }
}
// 4. 具體裝飾器:屬性附魔
class Strength extends WeaponDecorator {
public function getDescription(): string { return $this->weapon->getDescription() . ' +力量'; }
public function getCost(): float { return $this->weapon->getCost() + 30.0; }
}
class Agility extends WeaponDecorator {
public function getDescription(): string { return $this->weapon->getDescription() . ' +敏捷'; }
public function getCost(): float { return $this->weapon->getCost() + 50.0; }
}
class Intellect extends WeaponDecorator {
public function getDescription(): string { return $this->weapon->getDescription() . ' +智力'; }
public function getCost(): float { return $this->weapon->getCost() + 20.0; }
}
// 5. 附魔:鑲嵌寶石(展示多層裝飾)
class GemSocket extends WeaponDecorator {
private string $gemType;
private float $bonus;
public function __construct(Weapon $weapon, string $gemType, float $bonus) {
parent::__construct($weapon);
$this->gemType = $gemType;
$this->bonus = $bonus;
}
public function getDescription(): string { return $this->weapon->getDescription() . " +{$this->gemType}(+{$this->bonus})"; }
public function getCost(): float { return $this->weapon->getCost() + $this->bonus; }
}
<?php
// 基礎武器
$weapon = new Glave(); // 長劍 100
// 疊加屬性:力量 + 力量 + 敏捷
$weapon = new Agility(
new Strength(
new Strength($weapon)
)
);
echo $weapon->getDescription(); // 長劍 +力量 +力量 +敏捷
echo $weapon->getCost(); // 100 + 30 + 30 + 50 = 210
// 多層裝飾:武器 → 力量 → 寶石 → 敏捷 → 寶石
$weapon2 = new GemSocket(
new Agility(
new GemSocket(
new Strength(new Knife()),
'紅寶石', 100
),
'藍寶石', 80
)
);
echo $weapon2->getDescription(); // 匕首 +力量 +紅寶石(+100) +敏捷 +藍寶石(+80)
echo $weapon2->getCost(); // 80 + 30 + 100 + 50 + 80 = 340
<?php
// 條件裝飾器:僅當滿足條件時添加行為
class ConditionalDecorator extends WeaponDecorator {
private callable $condition;
private WeaponDecorator $decorator;
public function __construct(Weapon $weapon, callable $condition, WeaponDecorator $decorator) {
parent::__construct($weapon);
$this->condition = $condition;
$this->decorator = $decorator;
}
public function getDescription(): string {
return ($this->condition)($this->weapon) ? $this->decorator->getDescription() : $this->weapon->getDescription();
}
public function getCost(): float {
return ($this->condition)($this->weapon) ? $this->decorator->getCost() : $this->weapon->getCost();
}
}
// 使用:僅當武器基礎價值 > 150 時添加「神聖祝福」
$weapon = new ConditionalDecorator(
new Axe(),
fn(Weapon $w) => $w->getCost() > 150,
new class(new Axe()) extends WeaponDecorator { // 匿名裝飾器
public function getDescription(): string { return parent::getDescription() . ' +神聖祝福'; }
public function getCost(): float { return parent::getCost() + 200; }
}
);
Laravel HTTP 內核將請求處理封裝為中間件鏈,本質是裝飾器模式:
<?php
// 核心契約
interface HttpKernel {
public function handle(Request $request): Response;
}
// 基礎應用(核心組件)
class Application implements HttpKernel {
public function handle(Request $request): Response {
// 路由分發、控制器執行、視圖渲染...
return new Response('Hello World');
}
}
// 中間件基類(抽象裝飾器)
abstract class Middleware implements HttpKernel {
protected HttpKernel $next;
public function __construct(HttpKernel $next) {
$this->next = $next;
}
public function handle(Request $request): Response {
// 前置邏輯
$response = $this->next->handle($request);
// 後置邏輯
return $response;
}
}
// 具體中間件(具體裝飾器)
class AuthMiddleware extends Middleware {
public function handle(Request $request): Response {
if (!Auth::check()) return redirect('/login');
return parent::handle($request);
}
}
class CorsMiddleware extends Middleware {
public function handle(Request $request): Response {
$response = parent->handle($request);
$response->headers->set('Access-Control-Allow-Origin', '*');
return $response;
}
}
// 構建鏈(內核啟動時)
$app = new Application();
$app = new CorsMiddleware($app);
$app = new AuthMiddleware($app);
$app = new RateLimitMiddleware($app);
$app = new LoggingMiddleware($app);
// 處理請求
$response = $app->handle($request);
| 維度 | 裝飾器 | 繼承 | 策略模式 |
|---|---|---|---|
| 關係 | 組合 (Has-A) | 繼承 | 組合 |
| 靈活性 | 運行時動態疊加 | 編譯時固定 | 運行時切換整體算法 |
| 擴展維度 | 橫向添加職責 | 縱向特化 | 算法族替換 |
| 類爆炸 | 無 (N+M) | 有 (N×M) | 無 |
| 透明性 | 對客戶端透明 | 透明 | 客戶端知曉策略 |
new Log(new Cache(new Auth($app))) 表示「先 Auth,再 Cache,再 Log」unwrap() 或工廠重建鏈,便於測試與動態配置總結:裝飾器模式以組合替代繼承,為對象動態疊加職責。核心結構是「抽象組件介面 + 抽象裝飾器持有組件引用 + 具體裝飾器覆蓋/擴展行為」。它是構建中間件管道、流處理鏈、插件系統、UI 組件增強的標準模式。掌握「介面統一、單一職責、動態組合、順序語義」四要素,即可優雅解決繼承爆炸與靜態綁定難題。