命令模式
目录
0. 背景概述
用例1:现在我们使用的电视机已经相当的高端了,超薄,连接互联网,但是在上世纪80,90年代的时候电视机不仅笨重,而且接收的电视信号也好,只有一个按钮用来调频,如果我们想换个电视台怎么办?只能跑到电视机前面转动调频按钮,每次转换都需要人与电视机来交互,但是现在的电视机有了遥控器,可以直接遥控电视,将人与电视机的交互分割开来,这是一项很重要的技术变革。
用例2:历史上,秦始皇嬴政利用十年的时间统一六国,建立秦朝,这期间,作为君王,他殚精竭虑,大家想,统一六国管理一个国家需要做多少事情?如果都由嬴政一个人来干岂不是要累死了,怎么办?下指令交给下面的大臣来办,比如丞相李斯协助嬴政处理军政大事。嬴政就能够专注与国家大事,从繁琐的事务中抽离开来。
我们来分析以上两个用例,遥控的出现分离了人与电视机的交互,作为具体命令的请求者使得人与命令的接收者(电视机)解耦,李斯的存在,将嬴政与具体的军国大事的执行分离,嬴政颁布圣旨给李斯,李斯下达命令(命令请求者),指派给具体需要工作的人(命令接收者)。而在软件设计中,行为的请求者与行为的处理者大部分都是一种紧耦合的关系,不利于系统的扩展,也不符合设计模式的原则,命令模式将两者解耦,每个命令即一个操作,请求者发出命令要求执行一个操作,接收者接收命令执行这个操作,两者各司其职。
1. 命令模式介绍
1.1 命令模式定义
命令模式:(Command Pattern),将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。(定义取自 Java与模式一书)
1.2 命令模式UML结构图
Client:客户端角色,操作者的主要请求者
Invoker:执行者角色,接收客户端(Client)下达的具体指令,持有一个命令对象,调用命令对象来执行具体的命令操作。
Command:抽象命令接口,声明了所有具体的命令的公共行为。
ConcreteCommand:具体命令实现类,实现了抽象命令接口,关联了一个具体的命令接收者角色。
Receiver:具体的命令接收者,收到命令后执行此命令。
在用例1中,人就是Client,遥控器就是Invoker,遥控器上面的所有按键抽象为Command,具体的比如音量+,音量-等按键就是具体的ConcreteCommand,而电视机就是Receiver。
在用例2中,嬴政就是Client,丞相李斯就是Invoker,圣旨为Command,具体每道圣旨为具体的ConcreteCommand,而圣旨上面的执行人就是Receiver。
2. 命令模式的Java实现
本节将以用例2为例,使用Java实现命令模式,JDK版本为Java10。
2.1 UML结构图
Emperor:始皇嬴政
PrimeMinister:丞相李斯,传达始皇帝的命令。
Decree:抽象圣旨。
ConcreteDecreeFood:具体发布筹集粮草的圣旨。
ConcreteDecreeBattle:具体攻打赵国的圣旨。
Receiver:圣旨的接收者(这里为抽象类)。
DaSiNong:大司农,筹集粮草。
WangJian:大将军,攻打赵国。
2.2 Java用例代码
Decree抽象接口
public interface Decree {
void execute();
}
ConcreteDecreeFood实现
public class ConcreteDecreeFood implements Decree{
private Receiver receiver;
public ConcreteDecreeFood(Receiver receiver){
this.receiver=receiver;
}
@Override
public void execute() {
System.out.println("统一六国,命令"+this.receiver.getName()+"准备充足的粮草");
this.receiver.execute();
}
}
ConcreteDecreeBattle实现
public class ConcreteDecreeBattle implements Decree {
private Receiver receiver;
public ConcreteDecreeBattle(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void execute() {
System.out.println("攻打赵国,命令" + this.receiver.getName() + "为将,出征");
this.receiver.execute();
}
}
Receiver抽象类实现
public abstract class Receiver {
/**
* 圣旨接收人的名字
*/
private String name;
public Receiver(String name){
this.name=name;
}
/**
* 抽象执行方法
*/
public abstract void execute();
public String getName() {
return name;
}
}
WangJian具体接收者实现
public class WangJian extends Receiver {
public WangJian(String name) {
super(name);
}
@Override
public void execute() {
System.out.println("微臣" + this.getName() + "领命");
}
}
DaSiNong具体接收者实现
public class DaSiNong extends Receiver {
public DaSiNong(String name) {
super(name);
}
@Override
public void execute() {
System.out.println("臣"+this.getName()+"领命,必当征集足够粮草");
}
}
PrimeMinister实现
public class PrimeMinister {
/**
* 关联抽象圣旨对象
*/
private Decree decree;
/**
* 颁布具体圣旨
*/
public void publish(){
System.out.println("颁布圣旨");
this.decree.execute();
}
public Decree getDecree() {
return decree;
}
public void setDecree(Decree decree) {
this.decree = decree;
}
}
Emperor实现
public class Emperor {
public static void main(String[] args) {
//命令接收者大司农角色(忘记叫啥了,用XXX代替吧)
Receiver receiver=new DaSiNong("XXX");
//创建筹集粮草的圣旨
Decree decree=new ConcreteDecreeFood(receiver);
//圣旨交给李斯
PrimeMinister primeMinister=new PrimeMinister();
primeMinister.setDecree(decree);
primeMinister.publish();
//命令接收者大将军角色(忘记叫啥了,用XXX代替吧)
receiver=new WangJian("王翦");
//创建征战的圣旨
decree=new ConcreteDecreeBattle(receiver);
//交给李斯执行
primeMinister.setDecree(decree);
primeMinister.publish();
}
}
运行结果如下:
3.宏命令
经过10年征战,嬴政终于灭掉六国,建立大一统的秦朝,为了巩固自己的政权,秦始皇连续颁布了一系列圣旨,主要包括废分封,立郡县,统一文字和度量衡,修筑长城,此时对于这一系列的制度颁布,可以增加宏命令功能来实现。
宏命令是命令模式与组合模式的一起使用的产物,主要增加了一个组合命令接口,批量执行一系列的命令。
3.1 宏命令UML结构图
CompositDecree:圣旨组合接口,实现了Decree接口,由addDecree()与removeDecree()方法。
CompositDecreeImpl:组合接口的具体实现类。
EveryPeople:Receiver的实现类,秦朝子民。
3.2 java宏命令用例代码
EveryPeople代码
public class EveryPeople extends Receiver {
public EveryPeople(String name) {
super(name);
}
@Override
public void execute() {
System.out.println("好的,遵命");
}
}
CompositDecree代码
public interface CompositDecree extends Decree {
/**
* 增加一道圣旨
* @param decree
*/
void addDecree(Decree decree);
/**
* 删除一道圣旨
* @param decree
*/
void removeDecree(Decree decree);
}
CompositDecreeImpl代码
public class CompositDecreeImpl implements CompositDecree {
private List<Decree> decreeList = new ArrayList<>();
@Override
public void execute() {
decreeList.forEach(Decree::execute);
}
@Override
public void addDecree(Decree decree) {
decreeList.add(decree);
}
@Override
public void removeDecree(Decree decree) {
decreeList.remove(decree);
}
}
ConcreteDecreeA代码
public class ConcreteDecreeA implements Decree {
private Receiver receiver;
public ConcreteDecreeA(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void execute() {
System.out.println(this.receiver.getName()+",我们废除分封制度,设计郡县制度");
this.receiver.execute();
}
}
ConcreteDecreeB代码
public class ConcreteDecreeB implements Decree {
private Receiver receiver;
public ConcreteDecreeB(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void execute() {
System.out.println(this.receiver.getName()+",我们要统一文字,度量衡");
this.receiver.execute();
}
}
ConcreteDecreeC代码
public class ConcreteDecreeC implements Decree {
private Receiver receiver;
public ConcreteDecreeC(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void execute() {
System.out.println(this.receiver.getName()+",为了抵御匈奴,来吧,咱们修长城");
this.receiver.execute();
}
}
Emperor代码
public class Emperor {
public static void main(String[] args) {
//命令接收者秦朝子民角色
Receiver receiver=new EveryPeople("秦朝子民");
//创建宏命令
Decree compositDecree=new CompositDecreeImpl();
//创建废除分封制度圣旨
Decree decree=new ConcreteDecreeA(receiver);
((CompositDecreeImpl) compositDecree).addDecree(decree);
//创建统一文字制度圣旨
decree=new ConcreteDecreeB(receiver);
((CompositDecreeImpl) compositDecree).addDecree(decree);
//创建修筑长城圣旨
decree=new ConcreteDecreeC(receiver);
((CompositDecreeImpl) compositDecree).addDecree(decree);
//圣旨交给李斯
PrimeMinister primeMinister=new PrimeMinister();
primeMinister.setDecree(compositDecree);
primeMinister.publish();
}
}
运行结果如下:
4.命令模式的特点
优点:命令模式将请求的发出者与请求的接收者进行了解耦,新增加一个命令只需要扩展即可,体现了设计模式原则的开闭原则,系统能够保持很好的扩展性。而且命令模式与组合模式的联合可以批量的执行命令。
缺点:系统中,如果命令过多可能造成大量命令类的存在,使系统功能过于臃肿。
5.结语
本篇文章讲解了命令模式的基本结构以及用例,在具体的系统设计中,应该根据实际情况来设计,避免设计模式的滥用而导致系统更加的复杂