设计模式之Command命令模式


意图intent:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。

适用性:

  • 像上面讨论的MenuItem对象那样,抽象出待执行的动作以参数化某对象。你可用过程语言中的回调(callback)函数表达这种参数化机制。所谓回调函数是指函数先在某处注册,而它将在稍后某个需要的时候被调用。Command模式是回调机制的一个面向对象的替代品。
  • 在不同的时刻指定、排列和执行请求。一个Command对象可以有一个与初始请求无关的生存期。如果一个请求的接收者可用一种与地址空间无关的方式表达,那么就可将负责该请求的命令对象传送给另一个不同的进程并在那儿实现该请求。
  • 支持取消操作。CommandExecute操作可在实施操作前将状态存储起来,在取消操作时这个状态用来消除该操作的影响。Command接口必须添加一个Unexecute操作,该操作取消上一次Execute调用的效果。执行的命令被存储在一个历史列表中。可通过向后和向前遍历这一列表并分别调用UnexecuteExecute来实现重数不限的取消重做
  • 支持修改日志,这样当系统崩溃时,这些修改可以被重做一遍。在Command接口中添加装载操作和存储操作,可以用来保持变动的一个一致的修改日志。从崩溃中恢复的过程包括从磁盘中重新读入记录下来的命令并用Execute操作重新执行它们。
  • 用构建在原语操作上的高层操作构造一个系统。这样一种结构在支持事务(transaction)的信息系统中很常见。一个事务封装了对数据的一组变动。Command模式提供了对事务进行建模的方法。Command有一个公共的接口,使得你可以用同一种方式调用所有的事务。同时使用该模式也易于添加新事务以扩展系统。

Command命令模式,这个模式涉及的类还是挺多的哦,看起来还是有点麻烦。Invoker关联了commandcommand关联了receiverInvoker执行的Execute,调用了commandExecute,而commandexecute则调用了receiveraction。在软件构建过程中,行为请求着与行为实现这通常呈现一种紧耦合。但在某些场合比如需要对行为进行记录、撤销/重做(undo/redo)、事务等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将行为请求者与行为实现者解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。通过使用composite模式,可以将多个命令封装为一个复合命令MacroCommandCommand模式与c#中的Delegate有些类似。但两者定义行为借口的规范有所区别:command以面向对象中的接口-实现来定义行为接口规范,更严格,更符合抽象原则;delegate以函数签名来定义行为接口规范,更灵活,但抽象能力比较弱。能理解行为请求者与行为实现者吗?这里所谓的一组行为就是command中的execute了。行为的请求者是invoker,行为的实现者或者说是接收这就是receiver了。Over

 

 

DefinitionEncapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.

participants

    The classes and/or objects participating in this pattern are:

  • Command  (Command)
    • declares an interface for executing an operation
  • ConcreteCommand  (CalculatorCommand)
    • defines a binding between a Receiver object and an action
    • implements Execute by invoking the corresponding operation(s) on Receiver
  • Client  (CommandApp)
    • creates a ConcreteCommand object and sets its receiver
  • Invoker  (User)
    • asks the command to carry out the request
  • Receiver  (Calculator)
    • knows how to perform the operations associated with carrying out the request.

Sample code in c#

This structural code demonstrates the Command pattern which stores requests as objects allowing clients to execute or playback the requests.

// Command pattern -- Structural example

 

using System;

namespace DoFactory.GangOfFour.Command.Structural
{
  
  // MainApp test applicatio

  class MainApp
  {
    static void Main()
    {
      // Create receiver, command, and invoker
      Receiver receiver = new Receiver();
      Command command = new ConcreteCommand(receiver);
      Invoker invoker = new Invoker();

      // Set and execute command
      invoker.SetCommand(command);
      invoker.ExecuteCommand();

      // Wait for user
      Console.Read();
    }
  }

  // "Command"

  abstract class Command
  {
    protected Receiver receiver;

    // Constructor
    public Command(Receiver receiver)
    {
      this.receiver = receiver;
    }

    public abstract void Execute();
  }

  // "ConcreteCommand"

  class ConcreteCommand : Command
  {
    // Constructor
    public ConcreteCommand(Receiver receiver) :
      base(receiver)
    {  
    }

    public override void Execute()
    {
      receiver.Action();
    }
  }

  // "Receiver"

  class Receiver
  {
    public void Action()
    {
      Console.WriteLine("Called Receiver.Action()");
    }
  }

  // "Invoker"

  class Invoker
  {
    private Command command;

    public void SetCommand(Command command)
    {
      this.command = command;
    }

    public void ExecuteCommand()
    {
      command.Execute();
    }    
  }
}

 

Output

Called Receiver.Action()

 

 

This real-world code demonstrates the Command pattern used in a simple calculator with unlimited number of undo's and redo's. Note that in C#  the word 'operator' is a keyword. Prefixing it with '@' allows using it as an identifier.

// Command pattern -- Real World example

 

using System;
using System.Collections;

namespace DoFactory.GangOfFour.Command.RealWorld
{

  // MainApp test application

  class MainApp
  {
    static void Main()
    {
      // Create user and let her compute
      User user = new User();

      user.Compute('+', 100);
      user.Compute('-', 50);
      user.Compute('*', 10);
      user.Compute('/', 2);

      // Undo 4 commands
      user.Undo(4);

      // Redo 3 commands
      user.Redo(3);

      // Wait for user
      Console.Read();
    }
  }

  // "Command"

  abstract class Command
  {
    public abstract void Execute();
    public abstract void UnExecute();
  }

  // "ConcreteCommand"

  class CalculatorCommand : Command
  {
    char @operator;
    int operand;
    Calculator calculator;

    // Constructor
    public CalculatorCommand(Calculator calculator,
      char @operator, int operand)
    {
      this.calculator = calculator;
      this.@operator = @operator;
      this.operand = operand;
    }

    public char Operator
    {
      set{ @operator = value; }
    }

    public int Operand
    {
      set{ operand = value; }
    }

    public override void Execute()
    {
      calculator.Operation(@operator, operand);
    }

    public override void UnExecute()
    {
      calculator.Operation(Undo(@operator), operand);
    }

    // Private helper function
    private char Undo(char @operator)
    {
      char undo;
      switch(@operator)
      {
        case '+': undo = '-'; break;
        case '-': undo = '+'; break;
        case '*': undo = '/'; break;
        case '/': undo = '*'; break;
        default : undo = ' '; break;
      }
      return undo;
    }
  }

  // "Receiver"

  class Calculator
  {
    private int curr = 0;

    public void Operation(char @operator, int operand)
    {
      switch(@operator)
      {
        case '+': curr += operand; break;
        case '-': curr -= operand; break;
        case '*': curr *= operand; break;
        case '/': curr /= operand; break;
      }
      Console.WriteLine(
        "Current value = {0,3} (following {1} {2})",
        curr, @operator, operand);
    }
  }

  // "Invoker"

  class User
  {
    // Initializers
    private Calculator calculator = new Calculator();
    private ArrayList commands = new ArrayList();

    private int current = 0;

    public void Redo(int levels)
    {
      Console.WriteLine("/n---- Redo {0} levels ", levels);
      // Perform redo operations
      for (int i = 0; i < levels; i++)
      {
        if (current < commands.Count - 1)
        {
          Command command = commands[current++] as Command;
          command.Execute();
        }
      }
    }

    public void Undo(int levels)
    {
      Console.WriteLine("/n---- Undo {0} levels ", levels);
      // Perform undo operations
      for (int i = 0; i < levels; i++)
      {
        if (current > 0)
        {
          Command command = commands[--current] as Command;
          command.UnExecute();
        }
      }
    }

    public void Compute(char @operator, int operand)
    {
      // Create command operation and execute it
      Command command = new CalculatorCommand(
        calculator, @operator, operand);
      command.Execute();

      // Add command to undo list
      commands.Add(command);
      current++;
    }
  }
}

 

Output

Current value = 100 (following + 100)

Current value =  50 (following - 50)

Current value = 500 (following * 10)

Current value = 250 (following / 2)

 

---- Undo 4 levels

Current value = 500 (following * 2)

Current value =  50 (following / 10)

Current value = 100 (following + 50)

Current value =   0 (following - 100)

 

---- Redo 3 levels

Current value = 100 (following + 100)

Current value =  50 (following - 50)

Current value = 500 (following * 10)

 

 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值