设计模式之命令模式

我的理解:

            命令模式就是我给一个命令,不管你是怎么实现的,把最后正确的结果给我展示出来。

就像是windows下的dns命令,输入一个命令,得到对应的结果。
在这里插入图片描述

接下来我们来用一个我们会经常遇到的问题,制作一个项目

项目的成员分工采用了常规的分工方式,分为需求组(Requirement Group,RG)、美 工组(Page Group,PG)、代码组(我们内部还有一个比较优雅的名字:逻辑实现组,这里使 用大家经常称呼的名称,即Code Group,简称CG),加上我这个项目经理正好十个人。刚 开始,客户(也就是旅行社,甲方)很乐意和我们每个组探讨,比如和需求组讨论需求、和 美工讨论页面、和代码组讨论实现,告诉他们修改、删除、增加各种内容等。这是一种比较 常见的甲乙方合作模式,甲方深入到乙方的项目开发中,我们可以使用类图来表示这个过 程。

在这里插入图片描述

首先看抽象类Group

public abstract class Group {
//甲乙双方分开办公,如果你要和某个组讨论,你首先要找到这个组
public abstract void find();
//被要求增加功能
public abstract void add();
//被要求删除功能
public abstract void delete();
//被要求修改功能
public abstract void change();
//被要求给出所有的变更计划
public abstract void plan();
}

大家看抽象类中的每个方法,其中的每个都是一个命令语气——“找到它,增加,删
除,给我计划!”这些都是命令,给出命令然后由相关的人员去执行。我们再看3个实现类,
其中的需求组最重要,需求组RequirmentGroup类如代码清单15-2所示

public class RequirementGroup extends Group {
//客户要求需求组过去和他们谈
public void find() {
System.out.println(“找到需求组…”);
}
//客户要求增加一项需求
public void add() {
System.out.println(“客户要求增加一项需求…”);
}
//客户要求修改一项需求
public void change() {
System.out.println(“客户要求修改一项需求…”);
}
//客户要求删除一项需求
public void delete() {
System.out.println(“客户要求删除一项需求…”);
}
//客户要求给出变更计划
public void plan() {
System.out.println(“客户要求需求变更计划…”);
}
}

看美工组。美工组也很重要,是项目的脸面,客户最终接触到的还
是界面。美工组PageGroup类

public class PageGroup extends Group {
//首先这个美工组应该能找到吧,要不你跟谁谈?
public void find() {
System.out.println("找到美工组...");
}
//美工被要求增加一个页面
public void add() {
System.out.println("客户要求增加一个页面...");
}
//客户要求对现有界面做修改
public void change() {
System.out.println("客户要求修改一个页面...");
}
//甲方是老大,要求删除一些页面
public void delete() {
System.out.println("客户要求删除一个页面...");
}
//所有的增、删、改都要给出计划
public void plan() {
System.out.println("客户要求页面变更计划...");
}
}

最后看代码组。这个组的成员一般比较沉闷,不多说话,但多做事儿,这是这个组的典
型特点。代码组CodeGroup类

public class CodeGroup extends Group {
//客户要求代码组过去和他们谈
public void find() {
System.out.println("找到代码组...");
}
//客户要求增加一项功能
public void add() {
System.out.println("客户要求增加一项功能...");
}
//客户要求修改一项功能
public void change() {
System.out.println("客户要求修改一项功能...");
}
//客户要求删除一项功能
public void delete() {
System.out.println("客户要求删除一项功能...");
}
//客户要求给出变更计划
public void plan() {
System.out.println("客户要求代码变更计划...");
}
}

我们来分析一下为啥这样设计的。

Group抽象类 把三个组的共有的方法都给抽象出来,然后不同的组的子类去实现它。具体的业务方法,他们几个组都会有,用户说我要删除一个页面,代码组,页面组,需求组都会删除。

个Invoker类,其作用是根据客户的命令安排不同的组员进行工 作,例如,客户说“界面上删除一条记录”,Invoker类接收到该String类型命令后,通知美工 组PageGroup开始delete,然后再找到代码组CodeGroup后台不要存到数据库中,最后反馈给 客户一个执行计划。

在这里插入图片描述

但是客户的命令是一个String类型的,这有非常 多的变化,仅仅通过一个字符串来传递命令并不是一个非常好的方案,因为在系统设计中, 字符串没有约束力,根据字符串判断相关的业务逻辑不是一个优秀的解决方案。那怎么才是 一个优秀的方案呢?解决方案是:对客户发出的命令进行封装,每个命令是一个对象,避免 客户、负责人、组员之间的交流误差,封装后的结果就是客户只要说一个命令。
在这里插入图片描述

Command抽象类只有一个方法execute,其作用就是执行命令,子类非常坚决地实现该命 令,与军队中类似,上级军官给士兵发布命令:爬上这个旗杆!然后士兵回答:Yes,Sir!完 美的项目也与此类似,客户发送一个删除页面的命令,接头负责人Invoker接收到命令后,立 刻执行DeletePageCommand的execute方法。对类图中增加的几个类说明如下。

● Command抽象类:客户发给我们的命令,定义三个工作组的成员变量,供子类使用; 定义一个抽象方法execute,由子类来实现。

● CInvoker实现类:项目接头负责人,setComand接收客户发给我们的命令,action方法 是执行客户的命令(方法名写成是action,与command的execute区分开,避免混淆)。
其中,Command抽象类是整个扩展的核心,其源代码如代码清单15-7所示。

public abstract class Command {
//把三个组都定义好,子类可以直接使用
protected RequirementGroup rg = new RequirementGroup(); //需求组
protected PageGroup pg = new PageGroup(); //美工组
protected CodeGroup cg = new CodeGroup(); //代码组
//只有一个方法,你要我做什么事情
public abstract void execute();
}

抽象类很简单,具体的实现类只要实现execute方法就可以了。在一个项目中,需求增加
是很常见的,那就把“增加需求”定义为一个命令AddRequirementCommand类,如代码清单15-
8所示。

public class AddRequirementCommand extends Command {
//执行增加一项需求的命令
public void execute() {
//找到需求组
super.rg.find();
//增加一份需求
super.rg.add();
//给出计划
super.rg.plan();
}
}

页面变更也是比较频繁发生的,定义一个删除页面的命令 DeletePageCommand类,如代
码清单15-9所示

public class DeletePageCommand extends Command {
//执行删除一个页面的命令
public void execute() {
//找到页面组
super.pg.find();
//删除一个页面
super.rg.delete();
//给出计划
super.rg.plan();
}
}

Command抽象类可以有N个子类,如增加一个功能命令(AddFunCommand),删除一份
需求命令(DeleteRequirementCommand)等,这里就不再描述了,只要是由客户产生、时常
性的行为都可以定义为一个命令,其实现类都比较简单,
客户发送的命令已经确定下来,我们再看负责人Invoker,如代码清单15-10所示
负责人

public class Invoker {
//什么命令
private Command command;
//客户发出命令
public void setCommand(Command command){
this.command = command;
}
//执行客户的命令
public void action(){
this.command.execute();
}
}

这更简单了,负责人只要接到客户的命令,就立刻执行。我们模拟增加一项需求的过
程,如代码清单15-11所示。

public class Client {
public static void main(String[] args) {
//定义我们的接头人
Invoker xiaoSan = new Invoker(); //接头人就是小三
//客户要求增加一项需求
System.out.println("------------客户要求增加一项需求---------------");
//客户给我们下命令来
Command command = new AddRequirementCommand();
//接头人接收到命令
xiaoSan.setCommand(command);
//接头人执行命令
xiaoSan.action();
}
}

运行的结果
在这里插入图片描述
如果客户要求删除一个页面,我们的修改有多大

public class Client {
public static void main(String[] args) {
//定义我们的接头人
Invoker xiaoSan = new Invoker(); //接头人就是小三
//客户要求增加一项需求
System.out.println("------------客户要求删除一个页面---------------");
//客户给我们下命令来
//Command command = new AddRequirementCommand();
Command command = new DeletePageCommand();
//接头人接收到命令
xiaoSan.setCommand(command);
//接头人执行命令
xiaoSan.action();
}
}

是不是很简单,而且客户也不用知
道到底由谁来修改,高内聚的要求体现出来了,这就是命令模式。
命令模式的定义:
将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请
求排队或者记录请求日志,可以提供命令的撤销和恢复功能。
在这里插入图片描述
● Receive接收者角色
该角色就是干活的角色,命令传递到这里是应该被执行的,具体到我们上面的例子中就
是Group的三个实现类。
● Command命令角色
需要执行的所有命令都在这里声明。(不同的请求参数化)
● Invoker调用者角色
接收到命令,并执行命令。在例子中,我(项目经理)就是这个角色。

命令模式比较简单,但是在项目中非常频繁地使用,因为它的封装性非常好,把请求方
(Invoker)和执行方(Receiver)分开了,扩展性也有很好的保障,通用代码比较简单。我
们先阅读一下Receiver类,如代码清单15-13所示。
代码清单15-13 通用Receiver类
public abstract class Receiver {
//抽象接收者,定义每个接收者都必须完成的业务
public abstract void doSomething();
}
很奇怪,为什么Receiver是一个抽象类?那是因为接收者可以有多个,有多个就需要定
义一个所有特性的抽象集合——抽象的接收者,其具体的接收者如代码清单15-14所示。
代码清单15-14 具体的Receiver类
public class ConcreteReciver1 extends Receiver{
//每个接收者都必须处理一定的业务逻辑
public void doSomething(){
}
}
public class ConcreteReciver2 extends Receiver{
//每个接收者都必须处理一定的业务逻辑
public void doSomething(){
}
}
接收者可以是N个,这要依赖业务的具体定义。命令角色是命令模式的核心,其抽象的
命令类如代码清单15-15所示。
代码清单15-15 抽象的Command类
public abstract class Command {
//每个命令类都必须有一个执行命令的方法
public abstract void execute();
}
根据环境的需求,具体的命令类也可以有N个,其实现类如代码清单15-16所示。
代码清单15-16 具体的Command类
public class ConcreteCommand1 extends Command {
//对哪个Receiver类进行命令处理
private Receiver receiver;
//构造函数传递接收者
public ConcreteCommand1(Receiver _receiver){
this.receiver = _receiver;
}
//必须实现一个命令
public void execute() {
//业务处理
this.receiver.doSomething();
}
}
public class ConcreteCommand2 extends Command {
//哪个Receiver类进行命令处理
private Receiver receiver;
//构造函数传递接收者
public ConcreteCommand2(Receiver _receiver){
this.receiver = _receiver;
}
//必须实现一个命令
public void execute() {
//业务处理
this.receiver.doSomething();
}
}
定义了两个具体的命令类,读者可以在实际应用中扩展该命令类。在每个命令类中,通
过构造函数定义了该命令是针对哪一个接收者发出的,定义一个命令接收的主体。调用者非
常简单,仅实现命令的传递,如代码清单15-17所示。
代码清单15-17 调用者Invoker类
public class Invoker {
private Command command;
//受气包,接受命令
public void setCommand(Command _command){
this.command = _command;
}
//执行命令
public void action(){
this.command.execute();
}
}
调用者就像是一个受气包,不管什么命令,都要接收、执行!那我们来看高层模块如何
调用命令模式,如代码清单15-18所示。
代码清单15-18 场景类
public class Client {
public static void main(String[] args) {
//首先声明调用者Invoker
Invoker invoker = new Invoker();
//定义接收者
Receiver receiver = new ConcreteReciver1();
//定义一个发送给接收者的命令
Command command = new ConcreteCommand1(receiver);
//把命令交给调用者去执行
invoker.setCommand(command);
invoker.action();
}
}
一个完整的命令模式就此完成,读者可以在此基础上进行扩展。

封装后的演示
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奋斗中的代码猿--刘同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值