概述
日常生活中,我们出去吃饭都会遇到下面的场景:
命令模式定义:
将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开,这样两者之间通过命令对象进行沟通,这样方便将命令对象进行存储,传递,调用,增加与管理
结构
命令模式包含以下角色:
- 抽象命令类 (Abstract Command):定义命令的接口,声明执行的方法
- 具体命令角色 (Concrete Command) :具体的命令,实现命令接口;通常会持有接受者,并调用接受者的功能来完成命令要执行的操作
- 实现者 / 接收者 (Receiver) 角色:接收者,真正执行命令的对象,任何类都可能成为一个接收者,只要他能够实现命令要求的功能
- 调用者 / 请求者 (Invoker) 角色:要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,相当于使用命令对象的入口
案例
将上面的案例用代码实现,需要分析命令模式中的角色在该案例中由谁来充当
服务员:调用者角色,由她来发起命令,
资深大厨:接受者角色,真正命令执行的对象
订单:命令中包含订单
类图如下:
代码如下:
Order 类:订单类
package com.atguigu.command;
//订单类
import java.util.HashMap;
import java.util.Map;
public class Order {
//餐桌号码
private int diningTable;
//所下的餐品及份数
private Map<String,Integer> foodDic = new HashMap<String,Integer>();
public int getDiningTable() {
return diningTable;
}
public void setDiningTable(int diningTable) {
this.diningTable = diningTable;
}
public Map<String, Integer> getFoodDic() {
return foodDic;
}
public void setFood(String name,int num) {
foodDic.put(name,num);
}
}
SeniorChief 类:厨师类
package com.atguigu.command;
//厨师类
public class SeniorChief {
public void makeFood(String name,int num){
System.out.println(num + "份" + name + "已经完成");
}
}
Command :抽象命令接口
package com.atguigu.command;
//抽象命令接口
public interface Command {
void execute();
}
OrderCommand 类:具体命令类
package com.atguigu.command;
import java.util.Map;
import java.util.Set;
//具体命令类
public class OrderCommand implements Command{
//持有接收者对象
private Order order;
private SeniorChief receiver;
public OrderCommand(Order order,SeniorChief receiver){
this.order = order;
this.receiver = receiver;
}
@Override
public void execute() {
System.out.println(order.getDiningTable() + "桌的订单正在制作");
Map<String, Integer> foodDic = order.getFoodDic();
Set<String> keySet = foodDic.keySet();
for (String foodName : keySet) {
receiver.makeFood(foodName,foodDic.get(foodName));
}
System.out.println(order.getDiningTable() + "桌的订单已经完成");
}
}
Waitor 类:服务员类
package com.atguigu.command;
import java.util.ArrayList;
import java.util.List;
//服务员类 属于请求者
public class Waitor {
//持有多个命令对象
private List<Command> commands = new ArrayList<Command>();
//添加多个命令对象
public void setCommand(Command cmd){
commands.add(cmd);
}
//发起订单命令
public void orderUp(){
System.out.println("服务员:大厨,订单来了");
for (Command command : commands) {
if(command != null){
command.execute();
}
}
}
}
Client 类:测试类
package com.atguigu.command;
public class Client {
public static void main(String[] args) {
//创建第一个订单对象
Order order1 = new Order();
order1.setDiningTable(1);
order1.setFood("西红柿",2);
order1.setFood("可乐",3);
//创建第二个订单对象
Order order2 = new Order();
order2.setDiningTable(4);
order2.setFood("土豆片",1);
order2.setFood("冰封",2);
//创建厨师对象
SeniorChief receiver = new SeniorChief();
//创建命令对象
Command cmd1 = new OrderCommand(order1, receiver);
Command cmd2 = new OrderCommand(order2, receiver);
//创建服务员对象
Waitor invoker = new Waitor();
invoker.setCommand(cmd1);
invoker.setCommand(cmd2);
//服务员发起命令
invoker.orderUp();
}
}
服务员:大厨,订单来了
1桌的订单正在制作
3份可乐已经完成
2份西红柿已经完成
1桌的订单已经完成
4桌的订单正在制作
1份土豆片已经完成
2份冰封已经完成
4桌的订单已经完成
优点:
- 降低系统的耦合度,命令模式能够将调用操作的对象与实现该操作的对象解耦
- 增加或者删除命令非常方便,采用命令模式增加或者删除命令不会影响其他类,满足 “开闭原则”,对扩展比较灵活
- 可以实现宏命令,命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令
- 方便实现 Undo 和 Redo 操作,命令模式可以与备忘录模式结合,实现命令的撤销与恢复
缺点:
- 使用命令模式可能会导致某些系统有过多的具体命令类
- 系统结构更加复杂
使用场景
- 系统需要将 请求调用者 和 请求接收者 解耦,使得调用者和接收者不直接交互
- 系统需要在不同的时间制定请求,将请求排队和执行操作
- 系统需要支持命令的撤销 (Undo) 和 恢复 (Redo) 操作