最近想开始多学习一下工作中代码常常使用的设计模式。
除单例模式,工厂模式之外,command模式用的也是比较多的。
在学习了“正统”的设计模式之后,再来看实际项目代码中使用的类似的设计模式,发现还是有差别的——事实上,设计模式更多的是一种思想吧,如果凡事都寻求一个固定的模式,很容易陷入过度设计的误区。
设计模式是前人在开发过程中总结出来的经验,而并非一套标准,实际使用中,需要结合具体的业务来进行设计,而不应该拘泥于固定的模式。
Command模式
基本动机:解耦程序动作的发起与实际执行。
简单的例子:
战场上班长(Invoker,调用者)下达命令(Command):机枪掩护(具体的Comnand对象1),步兵冲锋(具体的Command对象2),但实际机枪怎么打,每个步兵(Receiver)怎么冲则属于执行的事,并非都由班长计划好的。不同的兵将会有不同的冲锋路径。Command模式的解耦机制一可实现多态,二可实现异步(动作发起并不意味着马上执行)。
再次梳理一下:
班长(Invoker)下达命令(Command)并指定命令的接收者(Receiver,也是命令的执行者)。之后班长喊“冲”(action),对应的Receiver便开始执行任务。
类图:
时序图:
代码示例:
命令接收者相关类:
// 接收者01,实现自己的业务逻辑
class Receiver01 {
public void doJob() {
System.out.println("接收者01 完成工作 ...\n");
}
}
// 接收者02,实现自己的业务逻辑
class Receiver02 {
public void doJob() {
System.out.println("接收者02 完成工作 ...\n");
}
}
命令类:
// 抽象命令类,定义了每个具体命令被执行的入口方法execute()
abstract class AbstractCommand {
public abstract void execute();
}
// 具体命令类01,通过构造函数的参数决定了该命令由哪个接收者执行
class Command01 extends AbstsractCommand {
private AbstractReceiver receiver = null;
public Command01(AbstractReceiver receiver) {
this.receiver = receiver;
}
public void execute() {
System.out.println("命令01 被发布 ...");
this.receiver.doJob();
}
}
// 具体命令类02,通过构造函数的参数决定了该命令由哪个接收者执行
class Command02 extends AbstractCommand {
private AbstractReceiver receiver = null;
public Command02(AbstractReceiver receiver) {
this.receiver = receiver;
}
public void execute() {
System.out.println("命令02 被发布 ...");
this.receiver.doJob();
}
}
调用者类:
// 调用者,负责将具体的命令传送给具体的接收者
class Invoker {
private AbstractCommand command = null;
public void setCommand(AbstractCommand command) {
this.command = command;
}
public void action() {
this.command.execute();
}
}
测试类:
//测试类
public class Client {
public static void main(String[] args) {
// 创建调用者
Invoker invoker = new Invoker();
// 创建一个具体命令,并指定接收并执行该命令的具体接收者
AbstractCommand command01 = new Command01(new Receiver01());
// 给调用者发布一个具体命令
invoker.setCommand(command01);
// 调用者执行命令,其实是将其传送给具体的接收者并让其真正执行
invoker.action();
AbstractCommand command02 = new Command01(new Receiver02());
invoker.setCommand(command02);
invoker.action();
}
}
测试结果:
命令01 被发布 …
接收者01 完成工作 …
命令02 被发布 …
接收者02 完成工作 …
特点
Command的模式最大的一个特点就是调用者与接收者的解耦。
实际应用中,Command的Receiver是多种多样的。比如文本编辑器,CopyCommand对应的是Edit控件的Copy接口,ExitCommand可能对应的就是Application的Exit接口,等等。通过Command模式,就可以实现调用的一致性,而Receiver可能还是千差万别的,因此需要在客户端 Client 类中显式指明出来。
项目中的command模式
前面说了,设计模式是从项目中抽象出来的,服务于项目。实际上我工作中遇到的command模式与上面讲的不太一样,大致如下:
一个command基类;
具体的command继承自command基类,对外只暴露execute接口。
业务线程持有多个具体command对象的指针,并在初始化线程的时候通过new操作获得实例化的command对象。
之后业务对外的接口中调用相应command的execute函数来处理具体的业务逻辑。
也就是说,这里并没有receiver这个角色,具体的执行任务由command来承担——这是基于业务本身的特点的。
另外,也没有使用Invoker——业务线程本身就是调用者了,没必要再多此一举进行这一层封装。
总之,先把死的模式学了,再在实际应用中用活起来!