設計模式是軟體開發的通用解決方案詞彙。本文詳細實作 觀察者模式、策略模式、簡單工廠模式,並說明適用場景與比較。
定義:物件間一對多依賴,主題狀態變更時自動通知所有觀察者。
適用:事件系統、訂閱通知、MVC View 更新、日誌記錄、快取失效。
<?php
// 1. 主題介面
interface Subject {
public function attach(Observer $observer): void;
public function detach(Observer $observer): void;
public function notify(): void;
public function setState(string $state): void;
public function getState(): string;
}
// 2. 具體主題:老闆
class Boss implements Subject {
private string $state = '';
/** @var Observer[] */
private array $observers = [];
public function attach(Observer $observer): void {
$this->observers[] = $observer;
}
public function detach(Observer $observer): void {
$key = array_search($observer, $this->observers, true);
if ($key !== false) unset($this->observers[$key]);
}
public function notify(): void {
foreach ($this->observers as $observer) {
$observer->update($this);
}
}
public function setState(string $state): void {
$this->state = $state;
$this->notify(); // 狀態變更自動通知
}
public function getState(): string {
return $this->state;
}
}
// 3. 觀察者介面
interface Observer {
public function update(Subject $subject): void;
}
// 4. 具體觀察者:股票觀察員
class StockObserver implements Observer {
private string $name;
public function __construct(string $name) {
$this->name = $name;
}
public function update(Subject $subject): void {
echo "[{$this->name}] 收到通知: 老闆狀態變為 '{$subject->getState()}',趕快跑!\n";
}
}
// 5. 客戶端使用
$boss = new Boss();
$observer1 = new StockObserver("三毛");
$observer2 = new StockObserver("小李");
$boss->attach($observer1);
$boss->attach($observer2);
// 重複加入會被允許 (可依需求去重)
$boss->setState("警察來了");
// 輸出:
// [三毛] 收到通知: 老闆狀態變為 '警察來了',趕快跑!
// [小李] 收到通知: 老闆狀態變為 '警察來了',趕快跑!
$boss->detach($observer1);
$boss->setState("安全了");
// 僅 [小李] 收到通知
?>
定義:定義演算法族群,封裝每個演算法,使它們可互換。讓演算法獨立於使用它的客戶端。
適用:支付方式、排序演算法、壓縮策略、路由演算法、驗證規則。
<?php
// 1. 策略介面
interface SortStrategy {
public function sort(array $data): array;
}
// 2. 具體策略
class BubbleSort implements SortStrategy {
public function sort(array $data): array {
$len = count($data);
for ($i = 0; $i < $len; $i++) {
for ($j = 0; $j < $len - $i - 1; $j++) {
if ($data[$j] > $data[$j+1]) {
[$data[$j], $data[$j+1]] = [$data[$j+1], $data[$j]];
}
}
}
return $data;
}
}
class QuickSort implements SortStrategy {
public function sort(array $data): array {
if (count($data) <= 1) return $data;
$pivot = $data[0];
$left = $right = [];
for ($i = 1; $i < count($data); $i++) {
$data[$i] < $pivot ? $left[] = $data[$i] : $right[] = $data[$i];
}
return array_merge($this->sort($left), [$pivot], $this->sort($right));
}
}
class NativeSort implements SortStrategy {
public function sort(array $data): array {
sort($data);
return $data;
}
}
// 3. 環境類別
class SortContext {
private SortStrategy $strategy;
public function __construct(SortStrategy $strategy) {
$this->strategy = $strategy;
}
public function setStrategy(SortStrategy $strategy): void {
$this->strategy = $strategy;
}
public function executeSort(array $data): array {
return $this->strategy->sort($data);
}
}
// 4. 客戶端使用
$data = [5, 2, 8, 1, 9, 3];
$context = new SortContext(new BubbleSort());
echo "Bubble: " . implode(',', $context->executeSort($data)) . "\n";
$context->setStrategy(new QuickSort());
echo "Quick: " . implode(',', $context->executeSort($data)) . "\n";
$context->setStrategy(new NativeSort());
echo "Native: " . implode(',', $context->executeSort($data)) . "\n";
?>
定義:由一個工廠類別根據參數決定建立哪個產品類別實例。屬創建型模式,非 GoF 23 種正式模式,但極實用。
適用:資料庫連接、日誌驅動、圖片處理器、API 客戶端、支付閘道。
<?php
// 1. 產品介面
interface Database {
public function connect(): void;
public function query(string $sql): array;
}
// 2. 具體產品
class MySQLDatabase implements Database {
public function connect(): void { echo "MySQL 連接成功\n"; }
public function query(string $sql): array { return ["MySQL 結果: $sql"]; }
}
class PostgreSQLDatabase implements Database {
public function connect(): void { echo "PostgreSQL 連接成功\n"; }
public function query(string $sql): array { return ["PostgreSQL 結果: $sql"]; }
}
class SQLiteDatabase implements Database {
public function connect(): void { echo "SQLite 連接成功\n"; }
public function query(string $sql): array { return ["SQLite 結果: $sql"]; }
}
// 3. 簡單工廠
class DatabaseFactory {
public static function create(string $type): Database {
return match(strtolower($type)) {
'mysql' => new MySQLDatabase(),
'postgresql' => new PostgreSQLDatabase(),
'sqlite' => new SQLiteDatabase(),
default => throw new InvalidArgumentException("不支援的資料庫類型: $type"),
};
}
}
// 4. 客戶端使用
$dbType = $_ENV['DB_TYPE'] ?? 'mysql'; // 從環境變數決定
$db = DatabaseFactory::create($dbType);
$db->connect();
$results = $db->query("SELECT * FROM users");
print_r($results);
?>
| 維度 | 觀察者模式 | 策略模式 | 簡單工廠模式 |
|---|---|---|---|
| 分類 | 行為型 | 行為型 | 創建型 (非 GoF) |
| 核心目的 | 物件間鬆耦合通知 | 演算法可互換 | 集中物件建立邏輯 |
| 關鍵角色 | Subject, Observer | Strategy, Context | Factory, Product |
| 耦合度 | 主題僅知介面 | 環境僅知策略介面 | 客戶端僅知工廠 |
| 擴展性 | 新增觀察者不修改主題 | 新增策略不修改環境 | 新增產品修改工廠 (違反 OCP) |
| 典型應用 | 事件總線、MVC | 排序、支付、壓縮 | DB連接、Logger、Driver |
<?php
// 策略 + 工廠:根據配置自動選擇策略
class PaymentContext {
private PaymentStrategy $strategy;
public function __construct(string $gateway) {
$this->strategy = PaymentFactory::create($gateway);
}
public function pay(float $amount): bool {
return $this->strategy->pay($amount);
}
}
// 觀察者 + 策略:不同觀察者使用不同策略處理通知
class EmailObserver implements Observer {
private NotificationStrategy $strategy;
public function __construct(NotificationStrategy $s) { $this->strategy = $s; }
public function update(Subject $s): void { $this->strategy->send($s->getState()); }
}
?>