您好,欢迎访问三七文档
命令模式•将把封装带到一个全新的境界:把方法调用(methodinvocation)封装起来•通过封装方法调用,可以把运算块包装成形•通过封装方法调用,也可以做一些很聪明的事情,例如记录日志,或者重复使用这些封装来实现撤销(undo)封装调用巴斯特家电自动化公司设计一个家电自动化遥控器的API附上一个创新控制器的原型以供研究这个遥控器具有7个可编程的插槽(每个都可以指定到一个不同的家电装置)每个插槽都有对应的开关按钮这个遥控器还具备一个整体的撤销按钮在光盘里附上有一组Java类,这些类是由多家厂商开发出来的,用来控制家电自动化装置,例如电灯、风扇、热水器、音响设备和其他类似的可控制装置希望能够创建一组控制遥控器的API,让每个插槽都能够控制一个或一组装置让硬件解脱!让我们看看这个遥控器…看一下厂商的类思考•目前有一个附着开和关按钮的简单遥控器,还有一套五花八门的厂商类•有许多类都具备on()和off()方法,除此之外,还有一些方法像是dim(),setTemperature(),setVolumn(),setDirection()•要把它看成分离的关注点:遥控器应该知道如何解读按钮被按下的动作,然后发出正确的请求,但是遥控器不需知道这些家电自动化的细节,或者如何打开热水器•但如果遥控器很笨,只知道如何做出一般的要求,那又怎能设计出让这个遥控器能够调用一些诸如打开电灯或车库门的动作?•不必让遥控器知道太多厂商类的细节•不想让遥控器包含一大堆if语句,例如“ifslot1==Light,thenlight.on(),elseifslot1==Hottubthenhottob.jetsOn()”•如果这样设计的话,只要有新的厂商类进来,就必须修改代码•命令模式可将“动作的请求者”从“动作的执行者”对象中解耦,请求者可以是遥控器,而执行者对象就是厂商类其中之一的实例•在设计中采用“命令对象”。利用它,把请求(例如打开电灯)封装成一个特定对象(例如客厅电灯对象)。•如果对每个按钮都存储一个命令对象,那么当按钮被按下时,就可以请命令对象做相关的工作•遥控器并不需要知道工作内容是什么,只要有个命令对象能和正确的对象沟通•使用模式,能够创建一个API,将这些命令对象加载到按钮插槽,让遥控器的代码尽量保持简单。而把家电自动化的工作和进行该工作的对象一起封装在命令对象中•这个模式可以同时设计“撤销按钮”餐厅餐厅的角色和职责•一张订单封装了准备餐店的请求•把订单想象成一个用来请求准备餐点的对象,和一般的对象一样,订单对象可以被传递:从女招待传递到订单柜台,或者从女招待传递到接替下一班的女招待•订单的接口只包含一个方法,也就是orderUp()•这个方法封装了准备餐点所需的动作•订单内有一个到“需要进行准备工作的对象”(也就是厨师)的引用•这一切都被封装起来,所以女招待不需要知道订单上有什么,也不需要知道是谁来准备餐点;她只需要将订单放到订单窗口,然后喊一声“订单来了”就可以了•女招待的工作是接受订单,然后调用订单的orderUp()方法•女招待的工作很简单:接下顾客的订单,继续帮助下一个顾客,然后将一定数量的订单放到订单柜台,并调用orderUp()方法,让人来准备餐点•女招待其实不必担心订单的内容是什么,或者由谁来准备餐点•她只需要知道,订单有一个orderUp()方法可以调用•现在,一天内,不同的顾客有不同的订单,这会使得女招待的takeOrder()方法被传入不同的参数•女招待知道所有的订单都支持orderUp()方法,任何时候她需要准备餐点时,调用这个方法快餐厨师具备准备餐点的知识•快餐厨师是一种对象,他真正知道如何准备餐点•一旦女招待调用orderUp()方法,快餐厨师就接手,实现需要创建餐点的所有方法•请注意,女招待和厨师之间是彻底的解耦:女招待的订单封装了餐点的细节,她只要调用每个订单的方法即可,而厨师看了订单就知道该做些什么餐点;厨师和女招待之间从来不需要直接沟通重点•把餐厅想成是OO设计模式的一种模型,而这个模型允许将“发出请求的对象”和“接受与执行这些请求的对象”分隔开来•对于遥控器API,我们需要分隔开“发出请求的按钮代码”和“执行请求的厂商特定对象”•万一遥控器的每个插槽都持有一个像餐厅订单那样的对象,会怎么样?•那么,当一个按钮被按下,只要调用该对象的orderUp()方法,电灯就开了,而遥控器不需要知道事情是怎么发生的,也不需要知道涉及哪些对象•现在我们就把餐厅的对话换成命令模式从餐厅到命令模式加载调用者•客户创建一个命令对象•客户利用setCommand()将命令对象储存在调用者中•稍后……客户要求调用者执行命令。请注意:一旦命令被加载到调用者,该命令可以被使用并丢弃,或者可以被保留下来并使用许多次连连看•餐厅命令模式女招待Command快餐厨师execute()orderUp()Client订单Invoker顾客ReceivertakeOrder()setCommand()第一个命令对象•实现命令接口(餐厅例子中是orderUp(),现在改为惯用名称execute)•publicinterfaceCommand{•publicvoidexecute();•}•实现一个打开电灯的命令•现在,假设想实现一个打开电灯的命令。根据厂商所提供的类•publicclassLightOffCommandimplementsCommand{•Lightlight;•publicLightOffCommand(Lightlight){•this.light=light;•}•publicvoidexecute(){•light.off();•}•}使用命令对象•让我们把这一切简化:假设我们有一个遥控器,它只有一个按钮和对应的插槽,可以控制一个装置•publicclassSimpleRemoteControl{•Commandslot;•publicSimpleRemoteControl(){}•publicvoidsetCommand(Commandcommand){•slot=command;•}•publicvoidbuttonWasPressed(){•slot.execute();•}•}遥控器使用的简单测试•publicclassRemoteControlTest{•publicstaticvoidmain(String[]args){•SimpleRemoteControlremote=newSimpleRemoteControl();•Lightlight=newLight();•LightOnCommandlightOn=new•LightOnCommand(light);remote.setCommand(lightOn);remote.buttonWasPressed();•}•}练习•实现GarageDoorOpenCommand•修改RemoteControlTest定义命令模式•命令模式将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象•命令模式也支持可撤销的操作•一个命令对象通过在特定接收者上绑定一组动作来封装一个请求•命令对象将动作和接收者包进对象中•这个对象只暴露出一个execute()方法,当此方能被调用的时候,接收者就会进行这些动作•从外面来看,其他对象不知道究竟哪个接收者进行了哪些动作,只知道如果调用execute()方法,请求的目的就能达到•利用命令来参数化对象的一些例子•再回到餐厅,一整天下来,女招待参数化许多订单•在简单遥控器中,我们先用一个“打开电灯”命令加载按钮插槽,稍后又将命令替换成为另一个“打开车库门”命令•就和女招待一样,遥控器插槽根本不在乎所拥有的是什么命令对象,只要该命令对象实现了Command接口就可以了类图•在简单遥控器(SimpleRemote)中所做的一样•需要提供一个方法,将命令指定到插槽•实际上,我们有7个插槽,每个插情都具备开和关按钮,所以我们可以用类似方式,把命令指定给遥控器,像这样•onCommands[0]=onCommand;•offCommands[0]=offCommand;将命令指定到插槽•将遥控器的每个插槽,对应到一个命令这样就让遥控器变成“调用者”•当按下按钮,相应命令对象的execute()方法就会被调用,其结果就是,接受者(例如:电灯、天花板电扇、音响)的动作被调用•实现遥控器•RemoteControl•实现命令•LightOffCommand•StereoOnWithCDCommand•逐步测试遥控器•RemoteLoader撤销的功能•当命令支持撤销时,该命令就必须提供和execute()方法相反的undo()方法•不管execute()刚才做什么,undo()都会倒转过来•在各个命令中加入undo()之前,我们必须先在Command接口中加入undo()方法•publicinterfaceCommand{•publicvoidexecute();•publicvoidundo();•}•从LightOnCommand开始下手:如果LightOnCommand的execute()方法被调用,那么最后被调用的是on()方法•undo()需要调用off()方法进行相反的动作–LightOnCommand–LightOffCommand•要加上对撤销按钮的支持,必须对遥控器类做一些小修改•我们打算这么做:加入一个新的实例变量,用来追踪最后被调用的命令,然后,不管何时撤销按钮被按下,我们都可以取出这个命令并调用它的undo()方法•RemoteControlWithUndo•QA•RemoteLoader使用状态实现撤销•实现电灯的撒销是有意义的,但也实在是太容易了•通常,想要实现撤销的功能,需要记录一些状态•让我们试一个更有趣的例子,比方说厂商类中的天花板上的吊扇•吊扇允许有多种转动速度,当然也允许被关闭•CeilingFan加入撤销到吊扇的命令类•让我们把撤销加入天花板吊扇的诸多命令中•需要追踪吊扇的最后设置速度,如果undo()方法被调用了,就要恢复成之前吊扇速度的设置值•CeilingFanHighCommand•如何实现low(低速)、medium(中速)、off(关闭)准备测试天花板吊扇•RemoteLoader每个遥控器都需具备Party模式•如果拥有了一个遥控器,却无法光凭按下一个按钮,就同时能弄暗灯光、打开音响和电视、设置好DVD.并让热水器开始加温,那么要这个遥控器还有什么意义?•制造一种新的命令,用来执行其他一堆命令……,而不只是执行一个命令MacroCommand•publicclassMacroCommandimplementsCommand{•Command[]commands;•publicMacroCommand(Command[]commands){this.commands=commands;•}•publicvoidexecute(){•for(inti=0;icommands.length;i++){commands[i].execute();•}•}•}使用宏命令•先创建想要进入宏的命令集合:•Lightlight=newLight(LivingRoom);•TVtv=newTV(“LivingRoom”);•Stereostereo=newStereo(LivingRoom);•Hottubhottub=newHottub();•LightOnCommandlightOn=newLightOnCommand(light);•StereoOnCommandstereoOn=newStereoOnCommand(stereo);•TVOnCommandtvOn=newTVOnCommand(tv);•HottubOnCommandhottubOn=newHottubOnCommand(hottub);•按下来创建两个数组,其中一个用来记录开启命令,另一
本文标题:命令模式
链接地址:https://www.777doc.com/doc-5905448 .html