用命令模式實現撤銷與恢復


命令模式實現撤銷與恢復

命令模式定義

將請求封裝成對象,以便使用不同的請求、隊列或日志來參數化其他對象。
命令對象可以把行動及參數封裝起來,於是這些行動可以被:

  • 重復多次
  • 取消
  • 恢復(取消后又再)

整個模式的類圖如下:

命令模式

通過 ICommand 接口,實現了控制類與調用者的解耦。


下面通過一個簡單的實例來詳細說明這種解耦以恢復撤銷是如何實現。

假定有一個風扇,當前有四個按鈕,分別是 高速模式 , 低速模式 , 撤銷恢復

風扇類類如下(對應類圖中的具體類 ConcreteClass):

有高速運轉、低速運轉等方法

public class CeilingFan
{
     public const int HIGH = 2;
     public const int LOW = 1;
     public const int OFF = 0;
     int speed;

     public CeilingFan() { speed = OFF; }
     public void High() { speed = HIGH; }
     public void Low() { speed = LOW; }
     public int getSpeed() { return speed; }
}

命令接口

public interface ICommand
{
    void execute();
    void undo();
}

風扇命令類 (Concrete)

// 高速運行類
public class CeilingFanHighCommand : ICommand
{
    CeilingFan ceilingFan; // 類中不用 new 方法創建類,降低耦合
    int preSpeed;  // 記錄執行按鍵前的狀態,便於回測
    public CeilingFanHighCommand(CeilingFan cf)
    {
        ceilingFan = cf;
    }

    public void execute()
    {
        preSpeed = ceilingFan.getSpeed();
        ceilingFan.High(); 
    }

    public void undo()
    {
        switch(preSpeed)
        {
            case CeilingFan.HIGH:
                ceilingFan.High();
                break;
            case CeilingFan.LOW:
                ceilingFan.Low();
                break;
            default:
                ceilingFan.Off();
                break;
        }
    }
}

// 低速運行類
public class CeilingFanLowCommand : ICommand
{
    CeilingFan ceilingFan; // 類中不用 new 方法創建類,降低耦合
    int preSpeed;  // 記錄執行按鍵前的狀態,便於回測
    public CeilingFanHighCommand(CeilingFan cf)
    {
        ceilingFan = cf;
    }

    public void execute()
    {
        preSpeed = ceilingFan.getSpeed();
        ceilingFan.Low(); 
    }

    public void undo()
    {
        switch(preSpeed)
        {
            case CeilingFan.HIGH:
                ceilingFan.High();
                break;
            case CeilingFan.LOW:
                ceilingFan.Low();
                break;
            default:
                ceilingFan.Off();
                break;
        }
    }
}

// 關閉類
public class CeilingFanLowCommand : ICommand
{
    CeilingFan ceilingFan; 
    int preSpeed;
    public CeilingFanHighCommand(CeilingFan cf)
    {
        ceilingFan = cf;
    }

    public void execute()
    {
        preSpeed = ceilingFan.getSpeed();
        ceilingFan.Off(); 
    }

    public void undo()
    {
        switch(preSpeed)
        {
            case CeilingFan.HIGH:
                ceilingFan.High();
                break;
            case CeilingFan.LOW:
                ceilingFan.Low();
                break;
            default:
                ceilingFan.Off();
                break;
        }
    }
}

以上風扇的相關命令構建后,需要被一個類來調用控制它,這個控制類不僅僅可以控制風扇,同時可以控制電燈,冰箱等等,所以不能與風扇耦合。

控制類 (對應類圖中 Control)

public class Control
{
    List<ICommand> onCommands;
    Stack<ICommand> undoCommands;
    Stack<ICommand> redoCommands;  // 記錄前一個命令, 便於 undo

    public Control()
    {
        onCommands = new List<ICommand>();
        undoCommands = new Stack<ICommand>();
        redoCommands = new Stack<ICommand>();
    }

    public void SetCommand(int slot, ICommand onCmd)
    {
        onCommands[slot] = onCmd;
    }

    public void OnButtonWasPressed(int slot)
    {
        if (onCommands[slot] != null)
        {
            onCommands[slot].execute();
            undoCommands.Push(onCommands[slot]);
        }
    }

    public void UndoButtonWasPressed() // 撤銷,此處用 stack 后進先出的特性
    {
        if (undoCommands.Count > 0)
        {
            ICommand cmd = undoCommands.Pop();
            redoCommands.Push(cmd);
            cmd.undo();
        }
    }

    public void RedoButtonWasPressed()
    {
        if(redoCommands.Count > 0)
        {
            ICommand cmd = redoCommands.Pop();
            undoCommands.Push(cmd);
            cmd.execute();
        }
    }
}

以上一個命令模式大體上完成了。
下面讓客戶進行調用測試

// 測試類 (類途中的 RemoteLoader)
class Program
{
    static void Main(string[] args)
    {
        CeilingFan cf = new CeilingFan();
        CeilingFanHighCommand cfh = new CeilingFanHighCommand(cf);
        CeilingFanLowCommand cfl = new CeilingFanLowCommand(cf);

        Control ctr = new Control();
        // 假設 button0, button1 分別為高速低速
        ctr.SetCommand(0, cfh);
        ctr.SetCommand(1, cfl);

        ctr.OnButtonWasPressed(0);
        ctr.OnButtonWasPressed(1);
        ctr.UndoButtonWasPressed();
        ctr.RedoButtonWasPressed();
    }
}

輸出內容如下:

turn ceilfan to high speed!

turn ceifan to low speed!

turn ceilfan to high speed!

turn ceifan to low speed!

撤銷與重做功能就此實現。整個過程中,最關鍵部分是命令對象的封裝以及控制類與具體工廠類耦合的解除。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM