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

PHP 設計模式實戰:命令模式封裝請求與支援撤銷操作

     編輯:PHP 綜合知識

前言

命令模式將「請求」封裝為物件,使請求可參數化、排隊、記錄、撤銷。適用於 GUI 按鈕、宏指令、交易回滾、工作佇列等場景。本文提供 PHP 介面導向實作,並示範撤銷功能。

角色定義

角色介面/類別職責
命令介面Command宣告 execute()、undo()
具體命令ConcreteCommand綁定接收者與動作,實作執行/撤銷邏輯
接收者Receiver執行實際業務操作
調用者Invoker持有命令物件,負責觸發執行/撤銷
客戶端Client組裝命令、接收者、調用者

完整程式碼(含撤銷支援)

<?php
// 1. 命令介面
interface Command {
    public function execute();
    public function undo();
}

// 2. 接收者:實際執行業務邏輯
class LightReceiver {
    private $location;
    private $isOn = false;
    private $brightness = 0;

    public function __construct(string $location) {
        $this->location = $location;
    }

    public function turnOn() {
        $this->isOn = true;
        $this->brightness = 100;
        echo "[{$this->location} 燈] 開啟,亮度 {$this->brightness}%\n";
    }

    public function turnOff() {
        $this->isOn = false;
        $this->brightness = 0;
        echo "[{$this->location} 燈] 關閉\n";
    }

    public function dim(int $level) {
        $this->brightness = $level;
        echo "[{$this->location} 燈] 調光至 {$level}%\n";
    }

    public function getState(): array {
        return ['on' => $this->isOn, 'brightness' => $this->brightness];
    }
}

// 3. 具體命令:開燈
class LightOnCommand implements Command {
    private $light;
    private $prevState;

    public function __construct(LightReceiver $light) {
        $this->light = $light;
    }

    public function execute() {
        $this->prevState = $this->light->getState(); // 記錄前狀態供撤銷
        $this->light->turnOn();
    }

    public function undo() {
        if ($this->prevState['on']) {
            $this->light->turnOn();
            $this->light->dim($this->prevState['brightness']);
        } else {
            $this->light->turnOff();
        }
        echo "[撤銷] 開燈命令 -> 恢復狀態\n";
    }
}

// 4. 具體命令:關燈
class LightOffCommand implements Command {
    private $light;
    private $prevState;

    public function __construct(LightReceiver $light) {
        $this->light = $light;
    }

    public function execute() {
        $this->prevState = $this->light->getState();
        $this->light->turnOff();
    }

    public function undo() {
        if ($this->prevState['on']) {
            $this->light->turnOn();
            $this->light->dim($this->prevState['brightness']);
        } else {
            $this->light->turnOff();
        }
        echo "[撤銷] 關燈命令 -> 恢復狀態\n";
    }
}

// 5. 調用者:支援命令歷史與撤銷
class RemoteControlInvoker {
    /** @var Command[] */
    private $history = [];
    private $lastCommand = null;

    public function setCommand(Command $command) {
        $this->lastCommand = $command;
    }

    public function pressButton() {
        if ($this->lastCommand) {
            $this->lastCommand->execute();
            $this->history[] = $this->lastCommand;
        }
    }

    public function pressUndo() {
        if ($this->lastCommand) {
            $this->lastCommand->undo();
            array_pop($this->history);
            $this->lastCommand = end($this->history) ?: null;
        }
    }
}

// 6. 客戶端測試
$livingRoomLight = new LightReceiver('客廳');
$bedroomLight    = new LightReceiver('臥室');

$livingOn  = new LightOnCommand($livingRoomLight);
$livingOff = new LightOffCommand($livingRoomLight);
$bedOn     = new LightOnCommand($bedroomLight);

$remote = new RemoteControlInvoker();

echo "--- 開客廳燈 ---\n";
$remote->setCommand($livingOn);
$remote->pressButton();

echo "\n--- 開臥室燈 ---\n";
$remote->setCommand($bedOn);
$remote->pressButton();

echo "\n--- 關客廳燈 ---\n";
$remote->setCommand($livingOff);
$remote->pressButton();

echo "\n--- 撤銷最後操作 (關客廳燈) ---\n";
$remote->pressUndo();

echo "\n--- 再次撤銷 (開臥室燈) ---\n";
$remote->pressUndo();

輸出結果

--- 開客廳燈 ---
[客廳 燈] 開啟,亮度 100%

--- 開臥室燈 ---
[臥室 燈] 開啟,亮度 100%

--- 關客廳燈 ---
[客廳 燈] 關閉

--- 撤銷最後操作 (關客廳燈) ---
[撤銷] 關燈命令 -> 恢復狀態
[客廳 燈] 開啟,亮度 100%

--- 再次撤銷 (開臥室燈) ---
[撤銷] 開燈命令 -> 恢復狀態
[臥室 燈] 關閉

核心優勢

  1. 請求物件化: 可儲存、傳遞、序列化、排隊(如工作佇列)。
  2. 解耦調用者與接收者: Invoker 不知具體業務邏輯,只呼叫 execute()。
  3. 支援撤銷/重做: 只要命令實作 undo() 並記錄前狀態即可。
  4. 組合命令(宏命令): 實作 MacroCommand 包含多個子命令,一次執行整個流程。
  5. 日誌/審計: 命令物件天然包含操作語意,易於記錄、重放。

宏命令範例

class MacroCommand implements Command {
    /** @var Command[] */
    private $commands = [];

    public function add(Command $cmd) { $this->commands[] = $cmd; }

    public function execute() {
        foreach ($this->commands as $cmd) $cmd->execute();
    }

    public function undo() {
        // 撤銷順序相反
        for ($i = count($this->commands)-1; $i >= 0; $i--) {
            $this->commands[$i]->undo();
        }
    }
}

// 使用:一鍵「離家模式」= 關所有燈 + 鎖門 + 啟動警報
$leaveHome = new MacroCommand();
$leaveHome->add(new LightOffCommand($livingRoomLight));
$leaveHome->add(new LightOffCommand($bedroomLight));
$leaveHome->add(new LockDoorCommand($door));
$leaveHome->add(new AlarmOnCommand($alarm));

$remote->setCommand($leaveHome);
$remote->pressButton(); // 一次執行全部

結語

命令模式將「做什麼」與「誰做、何時做」分離,是實現可撤銷操作、工作佇列、巨集指令、交易回滾的基石。PHP 介面 + 型別提示讓結構清晰、易於測試與擴展。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved