生活中的Command模式之在快餐店吃饭
以下内容是来自<<Head First In Design Pattern>>英文版,介绍Command模式那一章中的一个现实生活中的例子的部分内容,例子跟原文有点不一样,大部分是采用意译,有些地方可能翻译的不妥,还望指出.
(假设你到一个快餐吃饭,简化后的流程大概像上图所示的:1:你作为一个顾客(Customer),把菜单(Order)递给服务员(Waitress),2:服务员(Waitress)拿着菜单(Order),把它放到菜单柜台(对顾客点的菜单作进一步的处理)完了就说“菜单传给厨师的时候到了”3:快餐厨师(Short-Order Cook)按着菜单准备你所点的饭菜)
几个角色的交互情况
(交互由顾客发起,他知道自己想要的是什么也就是说点什么样的菜由他决定(create an order),比如说你对服务员说“我想要一个带奶酪的夹饼还要一个麦芽糖(?malt shake)”这样就有了方法reatOrder(),服务员拿着菜单,上面有顾客点的菜,把它传给菜单柜台处理后,她紧接着“把菜单传给厨师(orderUP())”(即调用orderUP()的方法,这时候的菜单上面写清楚了准备顾客所点的饭菜的所有说明。借助于这个菜单上的一个说明makeBurger(),厨师将会做出夹饼,类似地还会做出Shake。)
几个角色跟相应的职责
一个菜单(这里指的是服务员拿菜单到柜台处理以后形成的)封装了顾客的一个请求,即请求吃什么样的饭菜。
将这里的菜单看成是一个对象,一个担当着请求做顾客想要的饭菜的责任的对象。
跟任何对象一样,它可以被传递――从服务员到菜单处理柜台,或者是给下一个服务员接管,由她进行下一步处理。这个菜单有一个接口,它只有一个方法orderUp(),它封装了准备顾客所需的饭菜的所有行为。这个菜单还有个引用,指向的是需要做这些饭菜的对象(在个例子里,指的是厨师)。之所以要做这样的封装是因为要让服务员不必知道菜单里面的详细内容或者是甚至不用知道谁会来做这些饭菜(OK,在现实现生活中一个服务员可能会关心菜单上的详细内容还有谁来做,但是在这个例子里面不做这样的考虑。);她唯一需要做的是通过菜单处理柜台拿到处理过的菜单然后就说“找厨师去!(Order up!)”
上图是经过菜单处理柜台处理以后的菜单,上面显示了一个方法:
Public void orderUp(){
cookmakeBurger();
cookmakeShake();
}
服务员的职责是拿着菜单,然后调用它的方法
orderUp()
。
服务员的工作很简单:从顾客那里拿到他所点的饭菜的最原始的菜单,继续为顾客服务直到她把这个原始菜单交给菜单处理柜台为止,紧接着调用
方法
orderUp()
为顾客点的饭菜做准备。就像我们所发现的,在这个例子里面,服务员事实上不用担心菜单上的内容是什么或者说谁将要做这些饭菜;她只知道那个经过菜单处理柜台处理过后的菜单有一个方法
orderUp()可以给她来调用从而完成她的任务。
现在,每一天,这个服务员的
takeOrder()方法可以因为不同的顾客所点的饭菜的不同而
变成参数化了,但是这么多的顾客并不会给服务员的工作带来麻烦,因为她知道所有的菜单都支持orderUp()这个方法而且她可以在需要准备一个饭菜的任何时候来调用这个方法。
厨师知道如何做那些顾客所点的饭菜
厨师是真正知道怎么做那些顾客所点的饭菜的对象。一旦服务员调用方法orderUp(),
厨师实现所有需要做饭菜的方法。请注意服务员跟厨师之间是完全松耦合的:服务员有着菜
单,它封装了有关饭菜的详细内容;她知道在每个菜单上调用一个方法使顾客点的饭菜处于就绪状态。同时,厨师从服务员给她的菜单上得到有关顾客所点的饭菜的说明,这些是他所关心的;他从来都不用直接跟服务员交流。
从快餐店吃饭到命令模式
OK,我们已经花了足够的篇幅来叙述在快餐店吃饭所涉及到的角色以及他们之间的关系还有职责,相信大家对这方面已经很好的理解了。现在我们将重新分析下这个例子来映射到命令模式。你将会所有的角色都是一样的;只是名字改变了罢了。
从上图我们可对应每个对象跟方法和命令模式中的角色名字相对应。
Waitress(服务员)
|
invoker
|
Short Order Cook(厨师)
|
Receiver
|
orderUp(找厨师)
|
execute()
|
Order(菜单,还没经过处理的)
|
Command
|
Customer(顾客)
|
Client
|
takeOrder(从顾客那拿原始菜单)
|
setCommand()
|
命令模式的定义
到目前为止对于在命令模式中类和对象是如何交互我们已经有了很清楚的认识.现在我们将对命令模式进行定义。
首先我们先看看正式(GOF)的定义:
命令模式将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。
|
我们来仔细分析下上面的定义。我们知道一个命令对象封装了来自客户端的请求并且通过绑定对于接受者接受请求执行的一系列相应的动作来实现这个请求。为了达到这个目的,它将行为和接受者包装成一个对象只是对外暴露一个方法execute()。当这个方法被调用的时候,它从而委派接受者执行相应的动作。在外面看来,没有别的对象真正知道什么动作被哪个命令接受者执行了;他们只知道是否他们调用了execute()方法,他们的请求将会被实现。我们同样看到了可用不同的请求对客户进行参数化。回到上面的例子,服务员在一天里面通过不同的菜单而被参数化。目前我们还没有遇到的就是对请求排队或记录请求日志,以及支持可撤消的操作。我们不必担心,那些都是对基本的命令模式相当简单的扩展。
按照上面的思路,对应这个实例在下一篇文章里,我写了一个简单的代码,建议一起看,这样效果会更好。