情景
我想请舍友帮忙,帮我拿外卖上来。这个例子中,我即是命令的请求者Invoker,接收者自然就是舍友啦,roommateReceiver。命令即是让舍友帮忙拿外卖,命令可以有很多种,如买饮料啊,拿快递啊。所以通常设定为抽象类Command,具体的命令由其实现类实现。它的实现类要持有舍友的引用,这样才能让舍友帮忙拿外卖,舍友是具体实施操作,如拿外卖的具体对象。而“我”则持有命令的引用,只管对命令抽象类发出请求。
代码逻辑简单
以下凡是类或者方法中有字母“A”的地方,即代表拿外卖。
package com.zlfan.command;
public class Invoker {
private Command command;
public Invoker(Command command){
this.command = command;
}
public void requestA(){
command.executeA();
}
}
package com.zlfan.command;
public interface Command {
void executeA();
}
拿外卖命令(抽象类Command的实现类)
package com.zlfan.command;
public class ACommand implements Command {
RoommateReceiver receiver;
public ACommand(RoommateReceiver receiver){
this.receiver = receiver;
}
@Override
public void executeA() {
receiver.A();
}
}
package com.zlfan.command;
public class RoommateReceiver {
public void A(){
System.out.println("RoommateReceiver外卖帮忙拿到了");
}
}
客户端测试类:
package com.zlfan.command;
public class Client {
public static void main(String[] args) {
RoommateReceiver roommate = new RoommateReceiver();
Command command = new ACommand(roommate);
Invoker me = new Invoker(command);
me.requestA();
}
}
输出:
RoommateReceiver外卖帮忙拿到了
整个流程就是 我——命令抽象类——命令的真正的执行者(舍友)。
做到这里,可能有同学会提出了,干嘛写的这么复杂,直接创建舍友类RoommateReceiver,调用就是了。
的确是可以这样做,但是命令模式的初衷是让命令请求者和命令实现者解耦,方便对命令进行各种控制。
(以下内容,参考自这里)
打个比方:现在我们要对ConcreteCommandA与ConcreteCommandB以及其他一系列命令进行日志记录,并且两个命令之间的操作间隔不能大于1秒。
这种情况下要直接用两个类就会有大量的业务逻辑要在客户端进行处理,当命令增加,对每个命令的控制增加时,就会在Client里面产生大量的变化点,这样耦合就出来了,但是采用命令模式之后,对着一系列的命令我们都可以进行控制,这就是对变化点的封装。
实际Invoker代码如下:
public class Invoker
{
private ICommand lastCommand = null;
private DateTime lastDateTime = DateTime.Now;
public void RunCommand(ICommand command)
{
//记录操作日志
Console.WriteLine(command.GetType().Name);
//大于1秒,执行命令
if (lastCommand == null || (DateTime.Now - this.lastDateTime).TotalSeconds > 1)
{
lastCommand = command;
lastDateTime = DateTime.Now;
command.Execute();
}
//小于1秒时不执行,并进行相应处理
Console.WriteLine("操作间隔过短!");
}
}
适用场景
1. 命令的发送者和命令执行者有不同的生命周期。命令发送了并不是立即执行。
2. 命令需要进行各种管理逻辑。
3. 需要支持撤消/重做操作(这种状况的代码大家可以上网搜索下,有很多,这里不进行详细解读)。
优缺点
优:
- 降低耦合度。
- 新增一个命令/一组命令简单。
- 调用同一方法实现不同功能。
缺:会产生过多具体命令类。
结论
通过对上面的分析我们可以知道如下几点:
1. 命令模式是通过命令发送者和命令执行者的解耦来完成对命令的具体控制的。
2. 命令模式是对功能方法的抽象,并不是对对象的抽象。
3. 命令模式是将功能提升到对象来操作,以便对多个功能进行一系列的处理以及封装。
还可以通过组合模式结合在一起,将多个命令封装为一个“复合命令”。