我们都知道路边烧烤,在生意好的时候最赚钱。其实这时候也是烤肉师傅最难受的时候,为什么呢?忙的时候比较混乱,很容易造成分发错误、收钱错误、烧烤质量不佳等问题。相比,那些烤肉店呢,就不会出现这种情况,这是为什么呢?仔细想想,烤肉店都会有服务员,他们负责记录顾客的菜单。同时,如果顾客需要增加订单、取消订单。显然只需要告诉服务员,服务员则在日志(那本小本本)上修改即可。
这就是有没有服务员的不一样。看似没多大用处,实则用服务员来解耦客户和烤肉师傅。从而使‘行为请求者’与‘行为实现者’解耦。
模式定义
命令模式将请求封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式支持可撤销的操作。
命令模式可以对发送者和接受者完全解耦,发送者与接收者之间并没有直接的联系,发送者只需要知道如何发送请求,不需要关心请求是如何完成了。这就是命令模式,命令模式将方法调用给封装起来了。
模式结构
命令模式是对命令的封装。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。
每一个命令都是一个操作:请求的一方发出请求要求执行一个操作;接收的一方收到请求,并执行操作。命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的。
命令模式涉及到五个角色,它们分别是:
● 客户端(Client)角色:创建一个具体命令(ConcreteCommand)对象并确定其接收者。
● 命令(Command)角色:声明了一个给所有具体命令类的抽象接口。
● 具体命令(ConcreteCommand)角色:定义一个接收者和行为之间的弱耦合;实现execute()方法,负责调用接收者的相应操作。execute()方法通常叫做执行方法。
● 请求者(Invoker)角色:负责调用命令对象执行请求,相关的方法叫做行动方法。
● 接收者(Receiver)角色:负责具体实施和执行一个请求。任何一个类都可以成为接收者,实施和执行请求的方法叫做行动方法。
模式实现
package com.nchu.command;
abstract class Command {
protected Barbecuer receiver;
public Command(Barbecuer receiver) {
this.receiver = receiver;
}
public abstract void excuteCommand();
}
具体命令类
package com.nchu.command;
public class BakeChickenWingCommand extends Command {
@Override
public void excuteCommand() {
receiver.bakeChickenWing();
}
public BakeChickenWingCommand(Barbecuer receiver) {
super(receiver);
}
}
package com.nchu.command;
public class BakeMuttonCommand extends Command {
@Override
public void excuteCommand() {
receiver.bakeMutton();
}
public BakeMuttonCommand(Barbecuer receiver) {
super(receiver);
}
}
服务员类
package com.nchu.command;
import java.util.ArrayList;
import java.util.List;
public class Waiter {
private List<Command> orders = new ArrayList<>();
public void setOrder(Command command){
orders.add(command);
System.out.println("增加订单: " + command.toString() + " 时间:" + System.currentTimeMillis());
}
public void cancelOrder(Command command){
orders.remove(command);
System.out.println("取消订单: " + command.toString() + " 时间:" + System.currentTimeMillis());
}
public void Notify(){
for(Command cmd : orders){
cmd.excuteCommand();
}
}
}
测试类
package com.nchu.command;
public class Client {
public static void main(String[] args) {
//开店前准备
Barbecuer boy = new Barbecuer();
Command bakeMuttonCommandA = new BakeMuttonCommand(boy);
Command bakeMuttonCommandB = new BakeMuttonCommand(boy);
Command bakeMuttonCommandC = new BakeChickenWingCommand(boy);
Waiter girl = new Waiter();
//开门营业 顾客点餐
girl.setOrder(bakeMuttonCommandA);
girl.setOrder(bakeMuttonCommandB);
girl.setOrder(bakeMuttonCommandC);
//点菜完毕
girl.Notify();
}
}
使用场景
1.系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。
2.系统需要在不同的时间指定请求、将请求排队和执行请求。
3.系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。
4.系统需要将一组操作组合在一起,即支持宏命令。
模式优缺点
优点
1. 降低了系统耦合度
2. 新的命令可以很容易添加到系统中去。
3.支持命令的撤销操作和恢复操作。
缺点
使用命令模式可能会导致某些系统有过多的具体命令类。
模式总结
命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分隔开。