一、基础
命令模式(Command Pattern)将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。
命令模式的通用类图:
上面的类图中包含三个角色:
- Receive(接收者):该角色就是干活的角色,命令传递到这里是应该被执行的。
- Command(命令):需要执行的所有命令都在这里声明。
Invoker(调用者):接收到命令,并执行命令。
所谓对命令的封装,说白了,无非就是把一系列的操作写到一个方法中,然后供客户端调用就行了,反映到类图上,只需要一个`ConcreteCommand`类和`Client`类就可以完成对命令的封装,即使再进一步,为了增加灵活性,可以再增加一个`Command`类进行适当地抽象,这个调用者和接收者到底是什么作用呢? 其实大家可以换一个角度去想:假如仅仅是简单地把一些操作封装起来作为一条命令供别人调用,怎么能称为一种模式呢?命令模式作为一种行为类模式,首先要做到低耦合,耦合度低了才能提高灵活性,而加入调用者和接收者两个角色的目的也正是为此。
命令模式的通用代码如下:
class Receiver {
public void doSomething(){
System.out.println("接受者-业务逻辑处理");
}
}
abstract class Command {
public abstract void execute();
}
class ConcreteCommand extends Command {
private Receiver receiver;
public ConcreteCommand(Receiver receiver){
this.receiver = receiver;
}
public void execute() {
this.receiver.doSomething();
}
}
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){
Receiver receiver = new Receiver();
Command command = new ConcreteCommand(receiver);
//客户端直接执行具体命令方式(此方式与类图相符)
command.execute();
//客户端通过调用者来执行命令
Invoker invoker = new Invoker();
invoker.setCommand(command);
invoker.action();
}
}
通过代码我们可以看到,当我们调用时,执行的时序首先是调用者类,然后是命令类,最后是接收者类。也就是说一条命令的执行被分成了三步,它的耦合度要比把所有的操作都封装到一个类中要低的多,而这也正是命令模式的精髓所在:把命令的调用者与执行者分开,使双方不必关心对方是如何操作的。
命令模式的优点:
- 类间解耦:调用者角色与接收者角色之间没有任何依赖关系,调用者实现功能时只需调用
Command
抽象类的execute
方法就可以,不需要了解到底是哪个接收者执行。 - 可扩展性:
Command
的子类可以非常容易地扩展,而调用者Invoker
和搞成次的模块Client
不产生严重的代码耦合。 - 命令模式结合其他模式会更优秀:命令模式可以结合责任链模式,实现命令族解析任务;结合模板方法模式,则可以减少
Command
子类的膨胀问题。
命令模式的缺点:
- 有多少个命令,就需要创建多少个
Command
的子类,如果命令很多那么将变得非常臃肿;如果命令非常简单,实现起来就几行代码的事,而使用命令模式的话,不管命令多简单,都需要写一个命令类来封装。
命令模式的适用场景:对于大多数请求-响应模式的功能,比较适合使用命令模式,正如命令模式定义说的那样,命令模式对实现记录日志、撤销操作等功能比较方便。
二、拓展
apache commons chain 实现了责任链(Chain of Responsebility)模式 和 命令(Command)模式,其中的Catalog + 配置文件的方式使得调用方和Command的实现方的耦合度大大的降低,提高了灵活性。
更多关于Commons Chain的介绍可参考《》