相关知识:
通常设计模式有23种,主要分为三大类:
- 创建型模式(5种):单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式。
- 结构型模式(7种):适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
- 行为型模式(11种):模板方法模式、中介者模式、策略模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、解释器模式。
前言:
命令(Command)模式 属于设计模式中的行为模式。软件开发系统中,“方法的请求者”与“方法的实现者”之间经常存在紧密的耦合关系,这不利于软件功能的扩展与维护。而命令模式就能很好地解决这个问题。
在现实生活中,命令模式的例子也很多。比如看电视时,我们只需要轻轻一按遥控器就能完成频道的切换,这就是命令模式,将换台请求和换台处理完全解耦了。电视机遥控器(命令发送者)通过按钮(具体命令)来遥控电视机(命令接收者)。
再举一个我遇到的现实例子,我们公司开发需求的流程是:产品经理在jira上建立需求(看作是创建命令),然后将需求制定给我们开发(看作是命令和开发绑定起来),然后我们评估时间给产品,接着产品下命令开发。
优点:
- 引入统一的抽象动作降低系统的耦合度。
- 扩展性好,增加或删除命令非常方便。增加与删除命令不会影响其他类,满足“开闭原则”。
- 方便实现 Undo 和 Redo 操作。可以结合备忘录模式,实现命令的撤销与恢复。
- 可以在现有命令的基础上,增加额外功能。比如日志记录,结合装饰器模式会更加灵活。
缺点:
- 和抽象工厂一样,会产生大量具体的命令类(因为每一个具体操作都需要设计一个具体命令类),会增加系统的复杂性。
- 命令模式的结果其实就是接收方的执行结果,但是为了以命令的形式进行架构、解耦请求与实现,引入了额外类型结构(引入了请求方与抽象命令接口),增加了理解上的困难。
场景与实现:
命令模式包含以下主要角色。
- 抽象命令类(Command)角色:声明执行命令的接口,拥有执行命令的抽象方法 execute()。
- 具体命令类(Concrete Command)角色:是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。
- 实现者/接收者(Receiver)角色:执行命令功能的相关操作,是具体命令对象业务的真正实现者。
- 调用者/请求者(Invoker)角色:是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。
基于命令模式,实现一个我们现在开发流程的场景。
分析实现:
1.定义一个抽象命令类(命令),统一抽象方法execute()
2.创建两个具体命令类(一个具体的开始开发命令,一个具体的停止开发命令)
3.创建命令对应的接受者(开发)
4.创建命令的发起者,也就是调用者(产品经理)
5.创建测试类测试
代码实现:
项目结构图:
主要就是一个抽象命令类、两个具体命令实体,一个开发者实体类,一个产品经理实体类,测试类
相关类源码:
抽象命令类:ICommand,定义了一个抽象的命令执行方法
public abstract class ICommand {
// 定义命令的执行方法
public abstract void execute();
}
具体命令实体类——开始开发命令:StartDevelop
定义了一个开发者实体(命令接受者),并实现抽象类的命令执行方法
public class StartDevelop extends ICommand{
// 命令接受者
private Developer developer;
public StartDevelop(Developer developer){
this.developer = developer;
}
@Override
public void execute() {
System.out.println(developer.getName()+"开始开发需求");
}
}
具体命令实体类——停止开发命令:EndDevelop
也是同上定义了一个开发者实体(命令接受者),并实现抽象类的命令执行方法
public class EndDevelop extends ICommand {
// 命令接受者
private Developer developer;
public EndDevelop(Developer developer){
this.developer = developer;
}
@Override
public void execute() {
System.out.println(developer.getName()+"停止开发需求");
}
}
命令接受者——开发者:Developer,定义了开发者姓名
public class Developer {
private String name;
public Developer(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
命令调用者(执行者)——产品经理
定义一个命令抽象实体,用来接收命令,创建一个开始执行命令的方法。
public class TechnicalManager {
// 命令实体
private ICommand iCommand;
public void doCommand(){
iCommand.execute();
}
public ICommand getiCommand() {
return iCommand;
}
public void setiCommand(ICommand iCommand) {
this.iCommand = iCommand;
}
}
测试类 TestMain:
public class TestMain {
public static void main(String[] args) {
TechnicalManager technicalManager = new TechnicalManager();
// 创建开发者1(命令执行者)
Developer developer1 = new Developer("悟空");
// 创建命令1,并绑定开发者
ICommand command1 = new StartDevelop(developer1);
// 技术经理下达第一个命令
technicalManager.setiCommand(command1);
// 技术经理命令执行命令1
technicalManager.doCommand();
// 创建开发者2(命令执行者)
Developer developer2 = new Developer("八戒");
// 创建命令2,并绑定开发者
EndDevelop command2 = new EndDevelop(developer2);
// 技术经理下达第二个命令
technicalManager.setiCommand(command2);
// 技术经理命令执行命令2
technicalManager.doCommand();
}
}
主要就是先创建两个命令,两个开发者,并且把开发者和命令绑定起来(相当于产品把命令分给指定的人),接下来产品指定对应的命令进行执行。
测试结果。
这样命令模式就简单实现了。如果后续还需要添加命令,那么只需要新建一个具体的命令实体类就好了,扩展性是不是很好呢,
总结:
利用命令模式能够进行类的解耦,让调用者和接受者没有任何关系,也通过对行为的抽象,让新增其他行为变得清晰容易,也就 是可扩展性大大增加。