Java设计模式之《命令模式》
目录
1、介绍
1.1、命令模式定义
1.2、对比
1.3、典型应用场景
2、命令模式的结构
2.1、组成部分:
2.2、整体流程
3、实现
3.1、没有命令模式
3.2、命令模式写法
4、命令模式的优缺点
前言
java设计模式分类:
1、介绍
1.1、命令模式定义
命令模式(Command Pattern)是一种行为型设计模式,又叫动作模式或事务模式。
它将请求(命令)封装成对象,使得可以用不同的请求对客户端进行参数化,具体的请求可以在运行时更改、排队或记录,它将发出者和接收者解耦(顺序:发出者-->命令-->接收者)。
总结:
把请求的动作和请求的接收者进行解耦,让你能将动作封装成对象,再传递、存储、撤销或排队等。
1.2、对比
状态模式:
Context(电梯)-- 持有当前状态 --> State(状态接口)-- 实现 --> OpenState, RunState等
命令模式:
1.3、典型应用场景
1、Runnable接口:
Java中的Runnable接口就是一个典型的命令模式的应用。Runnable接口封装了需要执行的任务,然后可以交给线程去执行。
2、Timer和TimerTask类:
这两个类用于定时任务调度,TimerTask类封装了要执行的任务,然后由Timer类作为调用者执行这些任务。
3、Statement接口:
在Java中与数据库交互时,SQL语句被封装成Statement对象,然后由数据库驱动程序执行相应的命令。
生活示例:
1、 餐厅点餐:
在一家餐厅中,服务员充当调用者,厨师充当接收者,菜品可以作为具体命令。当顾客想点菜时,服务员会将顾客的需求封装成一个命令对象,并传递给厨师。厨师根据命令对象中的信息来完成相应的烹饪工作。这样,顾客和厨师之间不需要直接沟通,而是通过命令对象来实现点餐和烹饪的解耦。
2、遥控器控制家电:
拿电视遥控器举例,遥控器是调用者,电视是接收者。每个按键都可以看作是一个具体命令,例如音量加、音量减、切换频道等。
当用户按下某个按键时,遥控器会发送相应的命令给电视,然后电视根据命令执行相应的操作,如增加音量、减小音量或切换频道。
2、命令模式的结构
2.1、组成部分:
- Command(命令接口/抽象类): 声明
execute()
方法。 - ConcreteCommand(具体命令): 实现
Command
,并持有Receiver
引用,把请求操作的接收者封装进来,调用接收者的方法。 - Receiver(接收者): 真正执行业务逻辑的对象。
- Invoker(调用者): 有个成员变量保存Command,并执行
command.execute()
。 - Client(客户端): 组装命令、接收者、并给调用者下令。
2.2、整体流程
所有的“做某件事”,都变成订单(命令对象),Waiter只“发订单”,Chef只“实现订单”:
如下图所示:
命令模式,把“执行动作”的代码封装成对象。Waiter(服务员)不需要知道每个命令详情,只要保存一份“命令单子”,然后调用它的执行方法即可。
3、实现
假设你在饭店点菜。
- 你(Client 客户端):我要点“宫保鸡丁”。
- 服务员(Invoker 调用者):拿到菜单,把点菜单(订单)交给厨房。
- 订单(Command 命令对象):订单上写着“做宫保鸡丁”。
- 厨师(Receiver 接收者):收到订单,立刻开始炒菜。
这样,服务员并不需要会做菜,他只要拿订单就行了。
3.1、没有命令模式
你直接告诉服务员 “去让张师傅做宫保鸡丁”:
代码如下所示:
// 直接耦合
public class Waiter {private Chef chef = new Chef();public void orderGongBaoChicken() {chef.makeGongBaoChicken();}
}public class Chef {public void makeGongBaoChicken() {System.out.println("炒宫保鸡丁!");}
}
// 调用
Waiter waiter = new Waiter();
waiter.orderGongBaoChicken();
3.2、命令模式写法
1. 命令接口
public interface Command {void execute(); // 执行命令void undo(); // 撤销命令(可选)
}
- 这里的
Command
相当于“点菜单的模板”,所有的点菜单都要有一个execute方法。
2. 接收者(Receiver)——厨师
public class Chef {public void makeGongBaoChicken() {System.out.println("厨师:炒宫保鸡丁!");}public void makeFriedRice() {System.out.println("厨师:炒蛋炒饭!");}
}
- Chef能够真的炒不同的菜。
3. 具体命令(ConcreteCommand)——不同的订单
public class GongBaoChickenCommand implements Command {private Chef chef;public GongBaoChickenCommand(Chef chef) {this.chef = chef;}@Overridepublic void execute() {chef.makeGongBaoChicken();}@Overridepublic void undo() {System.out.println("取消宫保鸡丁!");}
}public class FriedRiceCommand implements Command {private Chef chef;public FriedRiceCommand(Chef chef) {this.chef = chef;}@Overridepublic void execute() {chef.makeFriedRice();}@Overridepublic void undo() {System.out.println("取消蛋炒饭!");}
}
- 每个命令就是一张“点菜单”,知道自己要交给哪个厨师(Receiver)。
4、调用者(Invoker)服务员
public class Waiter {private Command command;public void setCommand(Command command) {this.command = command;}public void placeOrder() {// 点菜command.execute();}public void cancelOrder() {// 撤销点菜command.undo();}
}
5、客户端组装
public class Customer {public static void main(String[] args) {Chef chef = new Chef();Command gongBaoChicken = new GongBaoChickenCommand(chef);Command friedRice = new FriedRiceCommand(chef);Waiter waiter = new Waiter();// 点宫保鸡丁waiter.setCommand(gongBaoChicken);waiter.placeOrder();// 点蛋炒饭waiter.setCommand(friedRice);waiter.placeOrder();// 撤销刚才的蛋炒饭waiter.cancelOrder();}
}
输出:
厨师:炒宫保鸡丁!
厨师:炒蛋炒饭!
取消蛋炒饭!
有上述示例可知命令模式的功能:
- 解耦:Waiter只需要认识命令Command,不用知道Chef做什么。
- 追加功能:要新增一道菜,只要做一个新的Command类即可,Waiter不用改。
- 支持队列、日志、撤销:命令对象能被存储、排队,可以撤销。
4、命令模式的优缺点
1、优点:
彻底解耦“请求者”与“执行者”,调用者无需知道接收者是啥。
请求可以参数化(每个对象带不同接收者和参数)。
请求可以放队列、做日志,如事务撤销与恢复、任务队列。
增加新命令类非常方便。
2、缺点:
类的数量可能会增多(每种命令都要实现 Command 接口)。
总结
命令模式适用于需要将请求封装成对象,实现请求发出者和接收者之间的解耦,并支持撤销、队列化和延迟执行的场景。它虽然可以提高系统的可扩展性和灵活性,但是需要权衡额外的开销和复杂性。
参考文章:
1、设计模式第19讲——命令模式(Command)-CSDN博客文章浏览阅读1.1w次,点赞38次,收藏174次。命令模式是一种行为设计模式,它将请求封装为对象,使发出者和接收者解耦。文章介绍了命令模式的角色(抽象命令、具体命令、接收者和调用者),优缺点以及在餐厅点餐和Java场景中的应用,还提供了代码实现示例。https://blog.csdn.net/weixin_45433817/article/details/131465270?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522d919eba72e6dd9b4ef68572ad0a68137%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=d919eba72e6dd9b4ef68572ad0a68137&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-131465270-null-null.142^v102^control&utm_term=%E5%91%BD%E4%BB%A4%E6%A8%A1%E5%BC%8F&spm=1018.2226.3001.4187