设计模式之禅-命令模式
目录
- 命令模式
- 例子
- 定义
- 优点
- 缺点
- 使用场景
- 扩展
- 增加需求
- 回滚
- 完美的Command
命令模式
例子
外包甲方项目。。刚开始,客户(也就是旅行社,甲方)很乐意和我们每个组探讨,比如和需求组讨论需求、和美工讨论页面、和代码组讨论实现,告诉他们修改、删除、增加各种内容等。这是一种比较 常见的甲乙方合作模式,甲方深入到乙方的项目开发中。
public abstract class Group {/*** 甲乙双方分开办公,如果你要和某个组讨论,你首先要找到这个组*/public abstract void find();/*** 被要求增加功能*/public abstract void add();/*** 被要求删除功能*/public abstract void delete();/*** 被要求修改功能*/public abstract void change();/*** 被要求给出所有的变更计划*/public abstract void plan();/*** 每个接收者都要对直接执行的任务可以回滚*/public void rollBack() {// 根据日志进行回滚}
}
public class CodeGroup extends Group{@Overridepublic void find() {System.out.println("找到代码组...");}@Overridepublic void add() {System.out.println("客户要求增加一项功能...");}@Overridepublic void delete() {System.out.println("客户要求删除一项功能...");}@Overridepublic void change() {System.out.println("客户要求修改一项功能...");}@Overridepublic void plan() {System.out.println("客户要求代码变更计划...");}
}
public class PageGroup extends Group{@Overridepublic void find() {System.out.println("找到美工组...");}@Overridepublic void add() {System.out.println("客户要求增加一个页面...");}@Overridepublic void delete() {System.out.println("客户要求删除一个页面...");}@Overridepublic void change() {System.out.println("客户要求修改一个页面...");}@Overridepublic void plan() {System.out.println("客户要求页面变更计划...");}
}
public class RequirementGroup extends Group{@Overridepublic void find() {System.out.println("找到需求组...");}@Overridepublic void add() {System.out.println("客户要求增加一项需求...");}@Overridepublic void delete() {System.out.println("客户要求删除一项需求...");}@Overridepublic void change() {System.out.println("客户要求修改一项需求...");}@Overridepublic void plan() {System.out.println("客户要求需求变更计划...");}
}
public class Client {public static void main(String[] args) {System.out.println("-----客户加需求-----");Group group = new RequirementGroup();group.find();group.add();group.plan();}
}
输出
-----客户加需求-----
找到需求组...
客户要求增加一项需求...
客户要求需求变更计划...
都由客户亲自去沟通,显然是行不通的。
在原有的类图上增加了一个Invoker类,其作用是根据客户的命令安排不同的组员进行工作。
Command抽象类只有一个方法execute,其作用就是执行命令,子类非常坚决地实现该命令。
- Command抽象类:客户发给我们的命令,定义三个工作组的成员变量,供子类使用;定义一个抽象方法execute,由子类来实现。
- CInvoker实现类:项目接头负责人,setComand接收客户发给我们的命令,action方法是执行客户的命令(方法名写成是action,与command的execute区分开,避免混淆)。
public abstract class Command {protected RequirementGroup rg = new RequirementGroup();protected PageGroup pg = new PageGroup();protected CodeGroup cg = new CodeGroup();/*** 执行*/public abstract void execute();
}
public class AddRequirementCommand extends Command{@Overridepublic void execute() {super.rg.find();super.rg.add();super.rg.plan();}
}
public class DeletePageCommand extends Command {@Overridepublic void execute() {super.pg.find();super.rg.delete();super.rg.plan();}
}
Command抽象类可以有N个子类,如增加一个功能命令(AddFunCommand),删除一份需求命令(DeleteRequirementCommand)等。
public class Invoker {/*** 什么命令*/private Command command;/*** 客户发出命令** @param command*/public void setCommand(Command command) {this.command = command;}/*** 执行客户的命令*/public void action() {this.command.execute();}
}
public class Client {public static void main(String[] args) {Invoker invoker = new Invoker();System.out.println("----客户加需求----");Command command = new AddRequirementCommand();invoker.setCommand(command);invoker.action();}
}
----客户加需求----
找到需求组...
客户要求增加一项需求...
客户要求需求变更计划...
定义
将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。
- Receive接收者角色
该角色就是干活的角色,命令传递到这里是应该被执行的,具体到我们上面的例子中就是Group的三个实现类。 - Command命令角色
需要执行的所有命令都在这里声明。 - Invoker调用者角色
接收到命令,并执行命令。在例子中,我(项目经理)就是这个角色。
public abstract class Receiver {/*** 抽象接收者,定义每个接受者都必须完成的任务*/public abstract void doSomething();
}
public class ConcreteReceiver1 extends Receiver{@Overridepublic void doSomething() {}
}
public class ConcreteReceiver2 extends Receiver{@Overridepublic void doSomething() {}
}
public abstract class Command {/*** 至少有一个执行命令*/public abstract void execute();
}
public class ConcreteCommand1 extends Command{private Receiver receiver;public ConcreteCommand1(Receiver receiver) {this.receiver = receiver;}@Overridepublic void execute() {this.receiver.doSomething();}
}
public class ConcreteCommand2 extends Command{private Receiver receiver;public ConcreteCommand2(Receiver receiver) {this.receiver = receiver;}@Overridepublic void execute() {this.receiver.doSomething();}
}
public class Invoker {private Command command;// 接收命令public void setCommand(Command command) {this.command = command;}// 执行命令public void action(){this.command.execute();}}
public class Client {public static void main(String[] args) {// 声明调用者Invoker invoker = new Invoker();// 定义接收者Receiver receiver = new ConcreteReceiver1();// 定义一个发送给接收者的命令Command command = new ConcreteCommand1(receiver);// 把命令交给调用者去执行invoker.setCommand(command);invoker.action();}
}
优点
- 类间解耦
调用者角色与接收者角色之间没有任何依赖关系,调用者实现功能时只需调用Command抽象类的execute方法就可以,不需要了解到底是哪个接收者执行。 - 可扩展性
Command的子类可以非常容易地扩展,而调用者Invoker和高层次的模块Client不产生严重的代码耦合。 - 命令模式结合其他模式会更优秀
命令模式可以结合责任链模式,实现命令族解析任务;结合模板方法模式,则可以减少Command子类的膨胀问题。
缺点
如果有N个命令,问题就出来了,Command的子类就可不是几个,而是N个,这个类膨胀得非常大。
使用场景
只要你认为是命令的地方就可以采用命令模式,例如,在GUI开发中,一个按钮的点击是一个命令,可以采用命令模式;模拟DOS命令的时候,当然也要采用命令模式;触发-反馈机制的处理等。
扩展
增加需求
通过命令,整合所有组的操作
public class AddRequirementCommend extends Command {/*** 新增需求*/@Overridepublic void execute() {// 找到需求组super.rg.find();// 增加需求super.rg.add();// 页面增加super.pg.add();// 功能增加super.cg.add();// 给出计划super.rg.plan();}
}
回滚
public abstract class Group {/*** 甲乙双方分开办公,如果你要和某个组讨论,你首先要找到这个组*/public abstract void find();/*** 被要求增加功能*/public abstract void add();/*** 被要求删除功能*/public abstract void delete();/*** 被要求修改功能*/public abstract void change();/*** 被要求给出所有的变更计划*/public abstract void plan();/*** 每个接收者都要对直接执行的任务可以回滚*/public void rollBack() {// 根据日志进行回滚}
}
public class CancelDeletePageCommand extends Command {/*** 撤销删除一个页面的命令*/@Overridepublic void execute() {super.pg.rollBack();}
}
完美的Command
每一个模式到实际应用的时候都有一些变形,命令模式的Receiver在实际应用中一般都会被封装掉(除非非常必要,例如撤销处理),那是因为在项目中:约定的优先级最高,每一个命令是对一个或多个Receiver的封装,我们可以在项目中通过有意义的类名或命令名处理命令角色和接收者角色的耦合关系(这就是约定),减少高层模块(Client类)对低层模块(Receiver角色类)的依赖关系,提高系统整体的稳定性。
public abstract class Command {/*** 定义一个子类的全局共享变量*/protected final Receiver receiver;/*** 实现类必须定义一个接收者* @param receiver receiver*/public Command(Receiver receiver) {this.receiver = receiver;}/*** 每个命令类都必须有一个执行命令的方法*/public abstract void execute();
}
public class ConcreteCommand1 extends Command {public ConcreteCommand1() {super(new ConcreteReceiver1());}public ConcreteCommand1(Receiver receiver) {super(receiver);}@Overridepublic void execute() {super.receiver.doSomething();}
}
public class ConcreteCommand2 extends Command {public ConcreteCommand2() {super(new ConcreteReceiver2());}public ConcreteCommand2(Receiver receiver) {super(receiver);}@Overridepublic void execute() {super.receiver.doSomething();}
}
public class Client {public static void main(String[] args) {Invoker invoker = new Invoker();Command command = new ConcreteCommand1();invoker.setCommand(command);invoker.action();}
}
这确实简化了很多,每个命令完成单一的职责,而不是根据接收者的不同完成不同的职责。在高层模块的调用时就不用考虑接收者是谁的问题。
设计模式之禅-命令模式相关推荐
- 【白话设计模式八】命令模式(Command)
为什么80%的码农都做不了架构师?>>> #0 系列目录# 白话设计模式 工厂模式 单例模式 [白话设计模式一]简单工厂模式(Simple Factory) [白话设计模式二] ...
- Head First 设计模式中的命令模式 的一个错误
最近在看Head First 设计模式,其中命令模式中有讲到实现撤销功能,并且作者还出了一道题, 下面的是书中习题: public class MarcoCommand implements Comm ...
- Head First 设计模式总结(六) 命令模式
本文总结了<Head First 设计模式>中的命令模式 命令模式--将请求封装成"对象",以便使用不同的请求.队列或者日志来参数化其他对象.命令模式也支持撤销操作. ...
- 【源码分析设计模式 13】命令模式
一.基本介绍 1.在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作时哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计 ...
- 设计模式笔记:命令模式
首先看看命令模式的定义:命令模式将请求封装成对象,以便使用不同的请求.队列或者日志来参数化其他对象.命令模式也支持撤销的操作. 所谓参数化,我的理解是实际执行的对象,比如light(电灯).strer ...
- Head First 设计模式笔记 5.命令模式
文章目录 设计要求 命令模式 实际实现 命令模式具体应用 设计要求 小明接到了新任务,要求设计一个遥控器,这个遥控器上面有七个插槽,每个插槽都有对应的开和关按钮.要求每个都能控制任意家电如灯,电视,电 ...
- [设计模式随意链接]——命令模式
1. 为什么要有命令模式 软件构建过程中.行为请求者与行为调用者可能有强耦合的情况.如下所示 if (type == a) { do_a() } else if (type == b) { do_b( ...
- 《Head First 设计模式》之命令模式——遥控器
命令模式(Command) --将"请求"封装成对象,以便使用不同的请求.队列或者日志来参数化其他对象.命令模式也支持可撤销的操作. 要点 将发出请求的对象和执行请求的对象解耦. ...
- 设计模式学习笔记——命令模式(Command)
1.特点:将请求发送者与具体实现者解耦,可对请求排列.取消.重做,支持事务.(多请求,单处理) 2.概念:属于对象的行为模式[GOF95].命令模式又称为行动(Action)模式或交易(Transac ...
最新文章
- js获取浏览器当前时间
- Meditation Guide
- CPU处理器架构和工作原理浅析
- 小米10Pro手机双击android,小米10Pro:不完美,但很小米。
- telnet命令发送邮件
- python都用什么写代码_python都用什么写代码
- 基础 - 字符读取函数scanf、getchar、gets、cin(清空缓存区解决单字符回车问题)
- C Tricks(九)—— 获取文件大小与申请数组空间
- 四川大学计算机绘图cad网站,计算机绘图: Auto CAD版
- AD14,原理图绘制引脚以及引脚名称的修改
- 【初等概率论】 01
- 男士黑色手表的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
- IDEA Maven下载依赖时报错: ERROR - #org.jetbrains.idea.maven - Cannot reconnect.
- android 彻底 关 亮度,Android设置屏幕亮度为0关闭屏幕 – 如何避免
- 讲讲Python爬虫绕过登录的小技巧
- js实现京东商城导航
- Java中append方法和add方法的区别
- 史上最全后端架构师技术图谱,值得收藏
- iOS 第三方分享、支付原生平台集成
- 牛逼,Python3竟然内置找茬神器!一起来找茬吧