初衷:
看了一些命令模式的博客,仍然不知道如何实现一个命令模式,如何设计,以及为何这么设计,本文通过一个例子来说明命令模式的设计过程以及这么设计的原因。
命令模式分解:
命令模式从组成上分为:
- 命令(Command)
- 调用者 (invoker)
- 客户端 (client)
- 命令接收者 (receiver)
其中,命令的具体实现类可以有多个,即可以有多个命令。
调用者为统一处理命令,让命令接收者执行命令,只有一个。
客户端为发起命令的主体。
命令接收者为具体的命令执行者。
接下来,我们看看具体的例子来理解何为命令模式,如何设计。
eg:PM要给RD提出各种各样的需求和修改,但需要在需求评审会上进行需求评审,由Leader拍板是不是做,PM说,RD,请实现APP的界面颜色随着用户手机壳颜色的变化而变化,Leader一听,有意思,做起来,然后给RD下达了命令,RD一看需求无能为力,将无法实现的结果告诉Leader,Leader将最终结果反馈给PM。
在这个例子中,PM(客户端)发出了命令,命令包括了两部分内容:
- RD(命令的接收者)
- APP界面颜色随着手机壳颜色变化而变化(接收者需要做的具体操作)
然后PM(客户端)不直接去找RD(receiver),需要找Leader(invoker)进行需求评审,Leader(invoker)需要作出一些判断或者前置后置处理,再让RD(接收者)执行具体的命令。
如何设计命令模式
这就是一个命令模式的例子,通过例子我们可以思考,如何设计命令模式:
参考上述命令模式的组成,我们分别设计:
设计命令:
有例子可以看出,一个具体的命令包括了具体的命令接收者和具体的执行操作,又由于可以有很多个命令,所以需要将命令进行抽象化,即接口或者抽象类:
public abstract class Command {
//每个命令类都必须有一个执行命令的方法
public abstract void execute();
}
具体的命令类需要包括两个部分,一是实现抽象类的方法,即命令的具体操作,二是新增命令的执行者属性。
public class ConcreteCommand extends Command {
//对哪个Receiver类进行命令处理
private Receiver receiver;
//构造函数传递接收者
public ConcreteCommand(Receiver _receiver){
this.receiver = _receiver;
}
//必须实现一个命令
public void execute() {
//业务处理
this.receiver.doSomething();
}
}
设计调用者:
根据上述的Leader(invoker),他需要知道PM的具体命令,所以他需要有一个命令的成员变量,然后他需要经过一些操作后让RD(接收者)开始执行命令,所以他一个执行方法。
public class Invoker {
private Command command;
//设置命令
public void setCommand(Command _command){
this.command = _command;
}
//执行命令
public void action(){
this.command.execute();
}
}
设计接收者:
RD(接收者)负责执行命令,需要对外暴露自己的能力,比如RD需要让PM知道自己会写bug,这样PM有写bug的需求才会给RD发出命令。又因为有好多RD,所以需要抽象一下,每个具体的RD继承该类。
public abstract class Receiver {
//抽象接收者,定义每个接收者都必须完成的业务
public abstract void doSomething();
}
设计客户端:
PM(客户端)是一个调用者,他负责发出一些无理的需求(命令,包括接收者和具体操作),然后告诉Leader(incoker),由Leader反馈给他最后的执行结果
public class Client {
public static void main(String[] args) {
//首先声明调用者Invoker
Invoker invoker = new Invoker();
//定义接收者
Receiver receiver = new ConcreteReciver();
//定义一个发送给接收者的命令
Command command = new ConcreteCommand(receiver);
//把命令交给调用者去执行
invoker.setCommand(command);
invoker.action();
}
}
优缺点
通过上面的设计可以看出,优点就是:解决了命令发起者和实现者直接的高耦合的关系,新添加一个命令也很容易。缺点是: 可能会因为命令的增多而导致命令类过多。