设计模式(16)命令模式

编程

  • 命令模式" title="命令模式">命令模式
  • 适用场景
  • Redo & Undo
  • 命令模式的优缺点

命令模式

命令模式是对一类对象公共操作的抽象,它们具有相同的方法签名,所以具有类似操作,可以被抽象出来,成为一个抽象的“命令”对象。请求以命令的形式包裹在对象中,并传给调用对象。调用者寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。这样实际操作的调用者就不是和一组对象打交道,它只需要依赖于这个“命令”对象的方法签名,并根据这个操作签名调用相关的方法。

GOF对命令模式描述为:

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

— Design Patterns : Elements of Reusable Object-Oriented Software

UML类图:

代码示例:

public interface ICommand

{

void Execute();

Receiver Receiver { set; }

}

public class Receiver

{

public string Name { get; private set; }

public string Address { get; private set; }

public void SetName()

{

this.Name = "Name";

}

public void SetAddress()

{

this.Name = "Address";

}

}

public abstract class CommandBase : ICommand

{

public Receiver Receiver { set; get; }

public abstract void Execute();

}

public class SetAddressCommand : CommandBase

{

public override void Execute()

{

base.Receiver.SetName();

}

}

public class SetNameCommand : CommandBase

{

public override void Execute()

{

base.Receiver.SetAddress();

}

}

public class Invoker

{

private IList<ICommand> commands = new List<ICommand>();

public void AddCommand(ICommand command)

{

commands.Add(command);

}

public void Run()

{

foreach (ICommand command in commands)

{

command.Execute();

}

}

}

Client代码:

Receiver receiver = new Receiver();

ICommand command1 = new SetNameCommand();

ICommand command2 = new SetAddressCommand();

command1.Receiver = receiver;

command2.Receiver = receiver;

Invoker invoker = new Invoker();

invoker.AddCommand(command1);

invoker.AddCommand(command2);

invoker.Run();

适用场景

  • 调用者同时与多个执行对象交互,而且每个操作可以抽象为近似的形式。
  • 我们需要控制调用本身的生命期,而不是调用者直截了当地进行一个调用,有可能根据需要合并、分配、疏导相关的调用。
  • 一系列类似的调用可能需要辅以Redo()或Undo()之类的特性。
  • 类似以往函数指针,需要在执行一个调用的同时告诉它需要回调那些操作。
  • 方法本身太过复杂,从整个项目重用的角度考虑,需要把方法的实现抽象为一组可以协作的对象。

Redo & Undo

再来看看如何用命令模式实现Redo和Undo,要实现Redo和Undo就需要保存执行过的命令,并通过安排这些命令的执行顺序来达到目地。

以SQL的执行为例,下面的代码定义了SQLExecute作为Receiver,CommandManager作为Invoker,InsertIntoCommand作为ConcreteCommand:

public interface ICommand

{

public void Execute();

public void Undo();

}

public class SQLExcute

{

public void InsertInto(string id)

{

Console.WriteLine("插入一条数据,id:" + id);

}

public void Delete(string id)

{

Console.WriteLine("删除一条数据,id:" + id);

}

}

public class InsertIntoCommand : ICommand

{

private SQLExcute sqlExcute;

private string id;

public InsertIntoCommand(SQLExcute sqlExcute, string id)

{

this.sqlExcute = sqlExcute;

this.id = id;

}

public void Execute()

{

sqlExcute.InsertInto(id);

}

public void Undo()

{

sqlExcute.Delete(id);

}

}

public class CommandManager

{

private Stack<ICommand> undoStacks = new Stack<ICommand>();

private Stack<ICommand> redoStacks = new Stack<ICommand>();

public void Execute(ICommand command)

{

command.Execute();

undoStacks.Push(command);

if (redoStacks.Count > 0)

{

redoStacks.Clear();

}

}

public void Undo()

{

if (undoStacks.Count > 0)

{

ICommand pop = undoStacks.Pop();

pop.Undo();

redoStacks.Push(pop);

}

}

public void Redo()

{

if (redoStacks.Count > 0)

{

ICommand pop = redoStacks.Pop();

pop.Execute();

}

}

}

Client代码:

CommandManager manager = new CommandManager();

SQLExcute excute = new SQLExcute();

InsertIntoCommand command1 = new InsertIntoCommand(excute, "1");

InsertIntoCommand command2 = new InsertIntoCommand(excute, "2");

manager.Execute(command1);

manager.Execute(command2);

Console.WriteLine("undo------------");

manager.Undo();

manager.Undo();

Console.WriteLine("redo------------");

manager.Redo();

manager.Redo();

运行结果:

插入一条数据,id:1

插入一条数据,id:2

undo------------

删除一条数据,id:2

删除一条数据,id:1

redo------------

插入一条数据,id:1

插入一条数据,id:2

命令模式的优缺点

可以看到使用命令模式,调用者并不需要直接与实际的执行者打交道,实现了两者的解耦,此外基于命令的机制,可以方便地做一些类似Undo, Redo的扩展,具体的优点有:

优点:

  • 命令模式将请求一个操作的对象与具体执行一个操作的对象分割开,符合开闭原则和迪米特法则
  • 能较容易的设计一个命令队列
  • 在需要的情况下,可以容易的将命令计入日志
  • 允许接收请求的一方决定是否接受请求
  • 可以容易的实现对请求的Undo,Redo
  • 由于加进新的具体命令类不影响其他的类,因此便于扩展

缺点:

命令模式也有其固有的缺点:在命令扩充至较多的数量时,便需要创建对应数量的ConcreteCommand,命令类过多,系统的维护会比较复杂。

参考书籍:

王翔著 《设计模式——基于C#的工程化实现及扩展》

以上是 设计模式(16)命令模式 的全部内容, 来源链接: utcz.com/z/519107.html

回到顶部