本文是对面向对象设计模式--命令模式(Command)的解析,主要分为定义解析、通过餐厅点餐案例、遥控器案例讲解命令模式、多案例练习加深对命令模式的理解、最后总结知识要点。
第一篇:定义解析
命令模式是GoF四人帮整理的《设计模式-可复用面向对象软件基础》一书中23种设计模式中归类为行为型模式中的设计模式,23种设计模式根据它们的用途分为三大类:5种创建型模式、7种结构型模式、11种行为型模式。
引自GoF四人帮对命令模式(Command)的定义与类图:
将一个请求封装为一个对象,从而使你可用不同的请求对象对客户进行参数化;对请求排队或记录请求日志,以及支持可取消的操作。
将一个请求封装为一个对象,从而使你可用不同的请求对象对客户进行参数化;这一句点出了命令模式的核心思想,客户发出一个请求就像发出一个命令或指指令一样,需要有接收者接收这个请求,然后进行处理。不同的请求,可能需要不同的接收者接收和处理,如智能家庭遥控器通过一个遥控器控制家里的各种电器,打开或关闭不同的电器,它的接收者是不同的,它必须由不同的电器去处理,为了实现将客户与这些具体接收者进行解耦,我们将请求封装成一个对象并增加一个调用者角色Invoker,将客户发出的不同请求封装成不同的命令对象,不同的命令对象包含着处理对应请求的各种接收者对象,将命令对象传递给调用者对象,由调用者对象去调用通知接收者处理请求,调用者就相当于遥控器,客户发出打开或关闭不同电器的请求,不同的请求被封装成不同的命令对象,遥控器调用不同的命令对象来通知不同的接收者即电器来处理这个打开或关闭的请求。这样也实现了用不同请求对客户进行参数化,同时也容易扩展,当有新的电器需要控制时,只需要为其封装一个对应的命令对象即可,客户端和调用者者不需要修改代码。
对请求排队或记录请求日志,以及支持可取消的操作;将请求封装成一个命令对象之后,可以在命令对象中添加日志,这些对象可以被传递或者使用队列进行排除执行,命令对象中可以实现相应的撤销方法,便可直接通过这个命令对象进行撤销请求。
第二篇:餐厅点餐案例
需求:客户填写点餐订单、餐厅服务员拿到订单,将订单放到订单柜台,并通知厨师开始准备餐点、厨师开始准备餐点、最后做出订单上的餐点。
需求分析:
一张订单封装了准备餐点的请求,把订单想象成一个用来请求准备餐点的对象,订单对象可以被传递;从女招待传递到订单柜台,然后喊一声“订单来了”就可以。
从面向对象角度分析点餐案例:
实现:
1、把订单封装成一个请求,订单请求中包含:餐点的内容、用于执行制作餐点的方法orderUp()和具体制作餐点的厨师的引用chef。
2、订单请求对象可以被传递,从客户传到了服务员,服务员调用订单中的执行制作餐点的方法orderUp()。
分析:
1)一张订单封装了准备餐点的请求,把订单想象成一个用来请求准备餐点的对象,订单对象可以被传递;
2)女招待的工作很简单,接下顾客的订单,然后将订单放到柜台,并调用orderUp()方法,通知厨师准备餐点。
3)厨师是一个对象,他真正知道如何准备餐点。一旦女招待调用orderUp()方法,厨师就接手,实现需要创建餐点的所有方法。
4)客户和厨师之间彻底解耦;女招待和厨师之间也彻底解耦;
通过这种实现方式:把“发出请求的对象”和“接受与执行这些请求的对象”分隔开来。
第三篇:遥控器案例
需求:
1)遥控器有七组开关按钮,还有一个撤销按钮。
2)这些开关按钮用来控制家电开关,如电灯、风扇、热水器、音响设备等。
3)我们希望能够创建一组遥控器的API,让每个开关按钮能够控制一个电器。
4)注意:要求提供扩展,能够方便未来添加新的电器控制开关。
需求分析:
1)目前有一个有很多开和关按钮的遥控器和很多不同的电器类。
2)分离关注点:遥控器应该知道如何解读按钮按下的动作,然后发出正确的请求;
3)但是遥控器不需要知道具体电器的处理细节,如如何打开热水器。
4)我们不必让遥控器知道太多具体电器类的细节。
采用命令模式实现:
1)命令模式可将“动作的请求者”从“动作的执行者”对象中解耦。请求者是用户、请求是按下的遥控器开关按钮的请求、执行者是具体的电器、遥控器是调用者。
2)客户发起开或关的请求、遥控器接收请求并调用相应的电器、电器进行相关的开或关操作。
3)把具体电器的开或关请求封装成请求对象,如打开电灯请求、关闭电灯请求等。每一个按扭就对应一个请求对象,按下按扭即调用请求的执行方法。
4)撤销按钮对应一个撤销请求,按下撤销按钮要调用对应请求的撤销方法。
当按下按钮的时候,就可以请命令对象做相关的工作。遥控器并不需要知道工作内容是什么,只要有个命令对象能和正确的对象沟通,把事情做好就可以了。所以,遥控器和电灯对象解耦了。
使用这个模式,我们能够创建一个API,将这些命令对象加载到对应的按钮开关,让遥控器的代码尽量保持简单。而把家电的工作和进行该工作的对象一起封装在命令对象中。
命令模式:
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可取消的操作。
1)Client客户:客户负责创建一个ConcreteCommand,并设置接收者Receiver。
2)Receiver接收者:知道如何进行必要的工作,实现这个接口,任何类都可以当接收者。
3)Command命令接口:为所有命令声明了一个接口。调用命令对象的execute()方法,就可以让接收者进行相关动作。这个接口也具备一个undo()撤销操作。
4)ConcreteCommand具体命令对象:定义了动作和接收者之间的绑定关系。调用者只要调用execute()就可以发出请求,然后由ConcreteCommand调用接收者的一个或多个动作。
5)Invoker调用者持有一个命令对象,并在某个时间点调用命令对象的execute()方法,将请求付诸实行。
6)命令对象将动作和接收者包进对象中。这个对象只暴露一个execute()方法,当此方法被调用的时候,接收者就会进行这些动作。从外面看,其他对象不知道究竟哪个接收者进行了哪些动作,只知道调用execute()方法,请求的目的就能达到。
1)命令对象将动作和接收者包进对象中。
2)这个对象只暴露一个execute()方法,当此方法被调用的时候,接收者就会进行这些动作。
3)从外面看,其他对象不知道究竟哪个接收者进行了哪些动作,只知道调用execute()方法,请求的目的就能达到。
第四篇:案例实践
练习案例源码:https://github.com/chentian114/100-dayaction/tree/master/designpattern-1
OrderDemo点餐案例:
客户点餐,填写订单,招待员拿到订单将其放到订单栏,并通知厨师制作餐点。
RemoteControlDemo遥控器案例:
一个遥控器有多个按钮,每对按钮控制一个电器的开和关,以及支持撤销操作。并希望能够提供扩展。
AudioPlayerDemo盒式收音机案例:
用户有一个盒式录音机,此录音机有播音(Play)、倒带(Rewind)和停止(Stop)功能。
案例分析:录音机的键盘便是请求者(Invoker)角色;客户是客户端角色,而录音机便是接收者角色。Command类扮演抽象命令角色,而PlayCommand、StopCommand和RewindCommand便是具体命令类。用户不需要知道播音(play)、倒带(rewind)和停止(stop)功能是怎么具体执行的,这些命令执行的细节全都由键盘(Keypad)具体实施。用户只需要在键盘上按下相应的键便可以了。
GroupRequirementDemo项目组需求案例:
客户会提不同的需求到项目组如:增加一个需求、修改一项需求、删除一个页面,项目组要对这些需求进行处理。项目组分为:需求组、美工组、研发组。增加一个需求需要需求组、美工组、研发组参与。删除一个页面需要需求组与研发组删除参与。
需求分析:
将客户需求封装成命令,客户只需要关注其所提的需求即可,而不需要关注项目组具体处理该任务的项目组内部信息。项目组长接到用户需求,对客户需求进行参数化,如:增加一个需求就是一个新增需求的命令,删除一个页面的需求就是一个删除页面的命令。命令里持有处理该任务的组目组成员的引用,并有一个统一的execute()方法调用具体项目组成员处理该需求。
实践:在实际Receiver一般采用封闭的方式,减少Client对Receiver的依赖。如案例中的各Group组。
第五篇:总结
命令模式:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。
Receive接收者角色,处理命令
Command命令角色,需要执行的请求
Invoker调用者角色,接收命令,并执行命令
命令模式把请求的发送者和请求的执行分割开,委派给不同的对象。
客户端:创建一个具体的命令。请求者:负责调用命令对象执行请求。接收者:负责具体实施和执行一个请求。
命令模式的优点:
1)使得新的命令很容易加入到系统中
2)能容易的设计一个命令队列
3)可以容易实现撤销
4)可以容易地将命令记入日志
命令模式优点:
1)更松散的耦合,将发起命令的客户端与具体处理命令的接收者完全解耦,客户端完全不知道接收者是什么样子。
2)更动态的控制,把请求封装起来,可以动态的对请求进行参数化、队列化和日志化等,使系统更灵活。
3)复合命令,很容易的组合命令,即宏命令,使系统功能更强大。
4)更好的扩展,很容易添加新的命令。
优点:
1)类间解耦,调用者角色与接收者角色之间没有任何依赖关系。
2)可扩展性,Command子类可以非常容易扩展,调用者Invoker和高层次的Client不产一严重的代码耦合。
缺点:
1)类数量随命令数量增长而增长。可能造成类数量过多。
所谓宏命令简单点说就是包含多个命令的命令,是一个命令的组合。