行为型设计模式——command命令模式第三组王洋龚雪娇张萌命令模式(Command)一.命令模式的由来二.命令模式意图和适用性三.命令模式结构四.实例说明五.命令模式效果分析一、命令模式的由来Command命令模式是一种对象行为型模式,它主要解决的问题是:在软件构建过程中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”的问题。如下图:当我们必须向某对象提交请求,但并不知道关于被请求的操作或请求的接受者的任何信息,此时无法抵御变化的紧耦合是不合适的。例如:需要对行为进行“记录、撤销/重做、事务”等处理。我们所要做的是将依赖关系转化,将紧耦合变为松耦合。则上图的形式转化为如下形式:由此我们知道引入命令模式的目的是解除命令发出者和接收者之间的紧密耦合关系,使二者相对独立,有利于程序的并行开发和代码的维护。二.命令模式意图和适用性意图:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可取消的操作。适用性:1、抽象出待执行的动作以参数化某对象2、在不同的时刻指定、排列和执行请求3、支持取消操作(Unexecute)4、支持修改日志5、用构建在原语操作上的高层操作构造一个系统三.命令模式结构命令模式结构图Command——命令抽象类声明执行操作的接口,并不执行这个接口,所有的具体命令都继承自命令抽象类。ConcreteCommand——具体命令类将一个接收者对象绑定于一个动作调用接收者相应的操作,以实现Execute方法。Client——客户端程序创建一个具体命令对象并设定它的接收者。Invoker——命令激活者将命令请求传递给相应的命令对象。Receiver——命令接受者知道如何实施与执行一个请求相关的操作。在结构图中,Command对象作为Invoker的一个属性,当点击事件发生时,Invoker调用方法Invoker()将请求发送给ConcreteCommand,再由ConcreteCommand调用Execute()将请求发送给Receiver,Client负责创建所有的角色,并设定Command与Invoker和Receiver之间的绑定关系。四.实例说明以一个简单的软件编辑器的设计为例,我们先来看一下没有使用命令模式的软件编辑器是如何实现的。如图所示:+Add(inMenuItem)Menu+Add(insoftware)Application+Clicked()MenuItem+Clicked()Editor+Clicked()Run+Open()+Close()+Editor()+link()+Run()Softwareusesuses+Add(inMenuItem)Menu+Add(inSoftware)Application+Clicked()-CommandCmdMenuItem+Execute()EditorCommand+Execute()RunCommand+Open()+Close()+Editor()+Link()+Run()Softwareusesuses+Execute()Command在新的结构中,每个MenuItem不直接向Software类发出消息,而是将请求先发送给与自己绑定在一起的命令对象,如EditorCommand和RunCommand,再由这些具体命令调用Software中的相关方法。这样一来,命令发送者与接收者之间的直接依赖关系,就改变为二者同时对命令对象的依赖关系。这也正体现了面向对象程序设计中“把变化的东西从稳定的东西中抽象出来”的思想,即响应请求的操作是有可能变化的,但客户端部分的代码应该是稳定的。使用命令模式,当Receiver发生变化时就不会影响到Invoker,因为与变化相关的代码被抽象到了Command中。下图给出了Editor命令与其激活者和接收者之间的关系,菜单项Editor是模式中的命令激活者,在点击事件发生后,它将命令请求发送给EditorCommand命令对象,EditorCommand再调用命令接收者Software的相关方法,这样一来命令的发出者与接收者之间由紧耦合变成了松耦合,这时如果我们想修改命令的相关操作,就不需要更改客户端的代码了,更加有利于代码的复用和维护。命令模式实现了命令的激活者(Invoker)与命令的接收者之间的解耦,体现了面向对象程序设计的一般思路。+Clicked()-CommandCmdMenuItem+Execute()-Software*docEditorCommand+Open()+Close()+Editor()+Link()+Run()Softwareuses+Execute()Command+Clicked()-CommandEditorCommandEditorEditorCommand-Execute();doc-Editor();uses软件菜单控件的代码实例首先定义Command抽象类,该类提供一个Execute接口,由具体命令类实现publicabstractclassCommand//抽象命令的基类{publicCommand(){}publicabstractvoidExecute();//定义一个Execute()接口}publicclassEditorCommand:Command//具体的编译命令类{publicEditorCommand(Softwaredoc)//绑定命令的接收对象{software=doc;}publicoverridevoidExecute(){//将请求发送给命令的接收者software.Editor();}privateSoftwaresoftware;}//具体的运行命令类publicclassRunCommand:Command{//绑定命令的接收对象publicRunCommand(Softwaredoc){software=doc;}publicoverridevoidExecute(){//将请求发送给命令的接收者software.Run();}privateSoftwaresoftware;}//命令的接收者Software提供Editor和Run的具体操作:publicclassSoftware{publicSoftware(){}publicvoidEditor(){//执行Editor命令Console.WriteLine(Editorfinished!);}publicvoidRun(){//执行Run命令Console.WriteLine(Copyfinished!);}}//抽象类MenuItem表示命令菜单,提供Clicked()接口publicabstractclassMenuItem{//定义Clicked接口publicabstractvoidClicked();}//CommandItem类,它维护一个Command对象,在本例中属于Invoker的角色publicclassCommandItem:MenuItem{publicCommandItem(Commandcmd){//绑定一个具体命令command=cmd;}publicoverridevoidClicked(){//将请求发送给命令对象command.Execute();}privateCommandcommand;}staticvoidMain(string[]args){//创建命令的接收者对象Softwaredoc=newSoftware();//绑定命令与接收者Commandccmd=newEditorCommand(doc);Commandpcmd=newRunCommand(doc);//绑定命令与激活者CommandItemcItem=newCommandItem(ccmd);CommandItempItem=newCommandItem(pcmd);//激活命令cItem.Clicked();pItem.Clicked();}程序运行结果如下:Copyfinished!Pastefinished!我们看到,命令的激活者首先发消息给具体命令,再由具体命令调用接收者的相关方法,得到了正确的输出结果。五.命令模式效果分析Command模式将调用操作的对象与知道如何实现该操作的对象解耦Command是头等的对象,它们可像其他的对象一样被操纵和扩展可将多个命令装配成一个复合命令可以很容易的增加新的Command对象,因为这无需改变已有的类。从定义可以看出备忘录模式是专门来存放对象历史状态的,这对于很好的实现undo、redo功能有很大的帮助。所以在命令模式中undo、redo功能可以配合备忘录模式来实现。