狀態模式 讓物件在內部狀態改變時改變行為,物件看起來像改變了類別。適用於:有限狀態機 (FSM)、遊戲角色狀態、訂單流程、TCP 連線狀態、文檔審核流程等。
attack())。<?php
// 狀態介面
interface TankState {
public function attack(Tank $tank): void;
public function move(Tank $tank, int $x, int $y): void;
public function getName(): string;
}
// 通常模式 (可移動、射程短、傷害低)
class TankMode implements TankState {
public function attack(Tank $tank): void {
echo "🚗 坦克[通常模式] 發射穿甲彈 → 傷害 30
";
}
public function move(Tank $tank, int $x, int $y): void {
$tank->x = $x; $tank->y = $y;
echo "🚗 移動至 ({$x}, {$y})
";
}
public function getName(): string { return '通常模式'; }
}
// 攻城模式 (不可動、射程遠、傷害高、範圍傷害)
class SiegeMode implements TankState {
public function attack(Tank $tank): void {
echo "💥 坦克[攻城模式] 發射電漿砲 → 傷害 70 (範圍 1.5)
";
}
public function move(Tank $tank, int $x, int $y): void {
echo "⛔ 攻城模式無法移動!需先解除攻城
";
}
public function getName(): string { return '攻城模式'; }
}
// 環境類別:坦克
class Tank {
public int $x = 0;
public int $y = 0;
public int $hp = 150;
private TankState $state;
public function __construct() {
$this->state = new TankMode(); // 初始狀態
}
// 狀態切換 (可加入冷卻、動畫等邏輯)
public function setState(TankState $state): void {
echo "🔄 狀態切換: {$this->state->getName()} → {$state->getName()}
";
$this->state = $state;
}
// 委派給當前狀態
public function attack(): void {
$this->state->attack($this);
}
public function move(int $x, int $y): void {
$this->state->move($this, $x, $y);
}
public function getStateName(): string {
return $this->state->getName();
}
}
// 使用示範
$tank = new Tank();
echo "初始: {$tank->getStateName()}
";
$tank->attack(); // 通常模式攻擊
$tank->move(100, 200); // 移動
$tank->setState(new SiegeMode()); // 部署攻城
$tank->attack(); // 攻城模式攻擊
$tank->move(150, 200); // 嘗試移動 (失敗)
$tank->setState(new TankMode()); // 解除攻城
$tank->move(150, 250); // 可移動
$tank->attack();
?>
<?php
interface State {
public function handle(Context $ctx, string $event): void;
}
class Context {
private State $state;
public function __construct(State $initial) { $this->state = $initial; }
public function setState(State $s): void { $this->state = $s; }
public function request(string $event): void { $this->state->handle($this, $event); }
}
class StateA implements State {
public function handle(Context $ctx, string $event): void {
match ($event) {
'go_b' => $ctx->setState(new StateB()),
'go_c' => $ctx->setState(new StateC()),
default => echo "StateA 忽略事件: {$event}
",
};
}
}
class StateB implements State {
public function handle(Context $ctx, string $event): void {
if ($event === 'back') $ctx->setState(new StateA());
}
}
class StateC implements State {
public function handle(Context $ctx, string $event): void {
if ($event === 'back') $ctx->setState(new StateA());
}
}
$ctx = new Context(new StateA());
$ctx->request('go_b'); // 切換到 B
$ctx->request('go_c'); // B 忽略
$ctx->request('back'); // 回到 A
?>
| 維度 | 狀態模式 | 策略模式 | if/else 型別檢查 |
|---|---|---|---|
| 狀態切換 | 內建 (Context 委派) | 外部注入 | 手動分支 |
| 狀態專屬資料 | 封裝在 State 類 | 封裝在 Strategy 類 | 分散在 Context |
| 新增狀態 | 新增 State 類 (OCP) | 新增 Strategy 類 | 修改分支 (違反 OCP) |
| 適用場景 | 物件行為隨狀態大幅改變、狀態間有轉換規則 | 演算法可互換、客戶端選擇 | 簡單、固定、少量分支 |
<?php
enum OrderStatus: string {
case Pending = 'pending';
case Paid = 'paid';
case Shipped = 'shipped';
case Delivered = 'delivered';
case Cancelled = 'cancelled';
public function canTransitionTo(self $next): bool {
return match($this) {
self::Pending => in_array($next, [self::Paid, self::Cancelled]),
self::Paid => in_array($next, [self::Shipped, self::Cancelled]),
self::Shipped => in_array($next, [self::Delivered]),
self::Delivered => false,
self::Cancelled => false,
};
}
}
$order = OrderStatus::Pending;
$order = $order->canTransitionTo(OrderStatus::Paid) ? OrderStatus::Paid : $order;
// 型別安全、自動完成、IDE 支援、不可變
?>
狀態模式將「狀態判斷分支」轉化為「多型派發」,新增狀態零修改既有代碼,讓複雜狀態機保持清晰可維護。