目录
一、命令模式介绍
1.1 命令模式定义
1.2 命令模式原理
1.2.1 命令模式类图
1.2.2 模式角色说明
二、命令模式的应用
2.1 需求说明
2.2 需求实现
2.2.1 抽象命令接口
2.2.2 订单类
2.2.3 厨师类
2.2.4 服务员类
2.2.5 具体命令类
2.2.6 测试类
三、命令模式总结
3.1 命令模式的优点
3.2 命令模式的缺点
3.3 命令模式的使用场景
一、命令模式介绍
1.1 命令模式定义
命令模式(command pattern)的定义: 命令模式将请求(命令)封装为一个对象,这样可以使用不同的请求参数化其他对象(将不同请求依赖注入到其他对象),并且能够支持请求(命令)的排队执行、记录日志、撤销等 (附加控制)功能。
命令模式的核心是将指令信息封装成一个对象,并将此对象作为参数发送给接收方去执行,达到使命令的请求与执行方解耦,双方只通过传递各种命令对象来完成任务。
在实际的开发中,如果你用到的编程语言并不支持用函数作为参数来传递,那么就可以借助命令模式将函数封装为对象来使用。
我们知道,C语言支持函数指针,我们可以把函数当作变量传递来传递去。但是,在大部分编程语言中,函数没法儿作为参数传递给其他函数,也没法儿赋值给变量。借助命令模式,我们可以将函数封装成对象。具体来说就是,设计一个包含这个函数的类,实例化一个对象传来传去,这样就可以实现把函数像对象一样使用。
1.2 命令模式原理
1.2.1 命令模式类图
1.2.2 模式角色说明
命令模式包含以下主要角色:
- 抽象命令类(Command)角色: 定义命令的接口,声明执行的方法。
- 具体命令(Concrete Command)角色:具体的命令,实现命令接口;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。
- 实现者/接收者(Receiver)角色: 接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。
- 调用者/请求者(Invoker)角色: 要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。
二、命令模式的应用
2.1 需求说明
模拟酒店后厨的出餐流程,来对命令模式进行一个演示,命令模式角色的角色与案例中角色的对应关系如下:
- 服务员: 即调用者角色,由她来发起命令。
- 厨师: 接收者,真正执行命令的对象。
- 订单: 命令中包含订单。
2.2 需求实现
2.2.1 抽象命令接口
package main.java.cn.test.command.V1;
/**
* @author ningzhaosheng
* @date 2024/1/15 17:42:27
* @description 抽象命令接口
*/
public interface Command {
//只需要定义一个统一的执行方法
void execute();
}
2.2.2 订单类
package main.java.cn.test.command.V1;
import java.util.HashMap;
import java.util.Map;
/**
* @author ningzhaosheng
* @date 2024/1/15 17:40:57
* @description 订单类
*/
public class Order {
//餐桌号码
private int diningTable;
//存储菜名与份数
private Map<String, Integer> foodMenu = new HashMap<>();
public int getDiningTable() {
return diningTable;
}
public void setDiningTable(int diningTable) {
this.diningTable = diningTable;
}
public Map<String, Integer> getFoodMenu() {
return foodMenu;
}
public void setFoodDic(Map<String, Integer> foodMenu) {
this.foodMenu = foodMenu;
}
}
2.2.3 厨师类
package main.java.cn.test.command.V1;
/**
* @author ningzhaosheng
* @date 2024/1/15 17:41:47
* @description 厨师类 -> Receiver角色
*/
public class Chef {
public void makeFood(int num, String foodName) {
System.out.println(num + "份," + foodName);
}
}
2.2.4 服务员类
package main.java.cn.test.command.V1;
import java.util.ArrayList;
/**
* @author ningzhaosheng
* @date 2024/1/15 17:44:20
* @description 服务员-> Invoker调用者
*/
public class Waiter {
//可以持有很多的命令对象
private ArrayList<Command> commands;
public Waiter() {
commands = new ArrayList();
}
public Waiter(ArrayList<Command> commands) {
this.commands = commands;
}
public void setCommands(Command command) {
commands.add(command);
}
//发出命令 ,指挥厨师工作
public void orderUp() {
System.out.println("服务员: 叮咚,有新的订单,请厨师开始制作......");
for (Command cmd : commands) {
if (cmd != null) {
cmd.execute();
}
}
}
}
2.2.5 具体命令类
package main.java.cn.test.command.V1;
import java.util.Set;
/**
* @author ningzhaosheng
* @date 2024/1/15 17:42:55
* @description 具体命令
*/
public class OrderCommand implements Command {
//持有接收者对象
private Chef receiver;
private Order order;
public OrderCommand(Chef receiver, Order order) {
this.receiver = receiver;
this.order = order;
}
@Override
public void execute() {
System.out.println(order.getDiningTable() + "号桌的订单: ");
Set<String> keys = order.getFoodMenu().keySet();
for (String key : keys) {
receiver.makeFood(order.getFoodMenu().get(key), key);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(order.getDiningTable() + "号桌的菜已上齐.");
}
}
2.2.6 测试类
package main.java.cn.test.command.V1;
/**
* @author ningzhaosheng
* @date 2024/1/15 17:45:22
* @description 测试类
*/
public class Test {
public static void main(String[] args) {
Order order1 = new Order();
order1.setDiningTable(1);
order1.getFoodMenu().put("鲍鱼炒饭", 1);
order1.getFoodMenu().put("茅台迎宾", 1);
Order order2 = new Order();
order2.setDiningTable(3);
order2.getFoodMenu().put("海参炒面", 1);
order2.getFoodMenu().put("五粮液", 1);
//创建接收者
Chef receiver = new Chef();
//将订单和接收者封装成命令对象
OrderCommand cmd1 = new OrderCommand(receiver, order1);
OrderCommand cmd2 = new OrderCommand(receiver, order2);
//创建调用者
Waiter invoke = new Waiter();
invoke.setCommands(cmd1);
invoke.setCommands(cmd2);
//将订单发送到后厨
invoke.orderUp();
}
}
三、命令模式总结
3.1 命令模式的优点
- 降低系统的耦合度。命令模式能将调用操作的对象与实现该操作的对象解耦。
- 增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,它满足“开闭原则”,对扩展比较灵活。
- 可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
3.2 命令模式的缺点
- 使用命令模式可能会导致某些系统有过多的具体命令类。
- 系统结构更加复杂。
3.3 命令模式的使用场景
- 系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。
- 系统需要在不同的时间指定请求、将请求排队和执行请求。
- 系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。
好了,本次分享就到这里,欢迎大家继续阅读《设计模式》专栏其他设计模式内容,如果有帮助到大家,欢迎大家点赞+关注+收藏,有疑问也欢迎大家评论留言!