在上学的时候,老师会把一个班的同学分成几个小组,并且每个小组选出一个组长,由组长带领自己的组员共同完成一个程序的开发。参加工作后,每个项目也都会有一个项目经理的角色,他们要做的事情就是同客户沟通需求并且制定最终需求,控制成本,把控进度,分配任务等等,可见一个项目经理对整个项目组是非常重要的。那没了项目经理会怎么样呢?接下来,我们用代码来模拟这个场景,看下缺少项目经理这个角色后,会存在怎样的问题。
假设我们现在要开发一个后台管理系统,我们的成员分为需求组、美工组、代码组。整个开发流程大致是这样的:客户首先与需求组讨论需求,然后和美工组讨论页面,最后和代码组讨论实现,告诉他们修改、删除、增加各种内容等。针对这种场景,我们设计出如下类图:
类图很简单,一个抽象组类,三个具体组继承自抽象组,客户Client分别与三个组进行交互。代码清单如下。
抽象组Group:
需求组RequirementGroup:
美工组:
代码组:
整个项目的3个支柱都已经产生了,那看客户怎么和我们谈。客户刚开始提交了他们自己写的一份比较完整的需求,需求组根据这份需求写了一份分析说明说,客户看后要求增加需求。代码如下:
运行结果如下所示:
客户的需求暂时满足了,过了一段时间,客户又要求删掉一个界面,于是又有一次场景变化:
运行结果如下所示:
过了一天后,客户又把代码组叫过去,说是数据库设计有问题,然后又叫美工组过去改页面,布置了一堆的命令……这时候,问题就慢慢浮现出来了。客户每次有问题都叫一个组过去,还要分析这个问题是需求还是页面还是代码,非常不方便,还会存在把美工组叫过来说页面画的不对,但美工说需求就是这样的,于是客户又要去找需求组谈,一次次的折腾,客户肯定会烦躁的。这时,就需要我们这面选举出一位项目经理了,客户不管有什么问题都找项目经理谈,然后项目经理根据客户的问题,分配给不同的组去处理,这样就能避免出现上面的问题了。说白了,就是客户发出命令,项目经理执行命令,具体怎么执行,由谁执行,由项目经理来确定。这就是所谓的命令模式。
将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能,这就是命令模式。
命令模式的通用类图如下:
在该类图中,有三个角色:
1. Receiver接收者角色
该角色就是干活的角色,命令传递到这里是应该被执行的,具体到我们上面的例子中就是Group的三个实现类。
2. Command命令角色
需要执行的所有命令都在这里声明。
3. Invoker调用者命令
接收到命令,并执行命令。项目经理就是这个角色。
它们的代码清单如下。
通用Receiver类:
具体接收者类:
接收者可以有N个,这要依赖业务的具体定义。
命令角色是命令模式的核心,其抽象的命令类如下:
具体的命令类也可以有N的,代码如下:
最后是调用者Invoker:
场景类:
一个完整的命令模式就此完成。
根据命令模式,我们对开头例子进行优化。首先,我们修改类图如下:
Command抽象类只有一个方法execute(),其作用就是执行命令。Invoker就相当于项目经理的角色,负责从客户那里获取需求,并按情况分配给对应的组进行处理。代码清单如下。
抽象命令类Command:
抽象类很简单,具体的子类只要重写execute方法就可以了。在一个项目中,增加需求是很常见的,那就把“增加需求”定义为一个命令AddRequirementCommand类:
同样,再定义一个删除页面命令类DeletePageCommand:
Command抽象类可以有N个子类,如增加一个功能(AddFunctionCommand)等,只要是由客户产生、时常性的行为都可以定义为一个命令,可以自行扩展。
接下来再看负责人Invoker类:
到这里,代码改造就完成了,我们模拟增加一项需求的过程:
运行结果如下所示:
如果客户要求删除一个页面呢:
运行结果如下所示:
可以看到,我们只需要修改设置命令那一行代码即可,非常简单。
命令模式具有以下优点:
1. 类间解耦
调用者角色与接收者角色之间没有任何依赖关系,调用者实现功能时只需调用Command抽象类的execute方法就可以,不需要了解到底是哪个接收者执行。
2. 可扩展性
Command的子类可以非常容易地扩展,而调用者Invoker和高层次的模块Client不产生严重的代码耦合。
命令模式也是有缺点的,就是如果有N个命令,Command的子类就是N个,这个类膨胀的非常大,需要在项目中慎重考虑使用。
实际开发中,只要你认为是命令的地方就可以采用命令模式。例如,一个按钮的点击是一个命令,就可以采用命令模式。
网站声明:如果转载,请联系本站管理员。否则一切后果自行承担。
添加我为好友,拉您入交流群!
请使用微信扫一扫!