命令模式(Command Pattern)
命令模式(Command Pattern)是一种行为型设计模式,它将请求(命令)封装成对象,从而使您能够参数化客户端(调用者)使用不同的请求、队列请求或者日志请求,甚至支持可撤销的操作。
核心思想是 将请求封装成对象,从而使得调用者与接收者解耦,同时支持请求的存储、撤销、重做和排队执行。
主要组成部分
- Command(命令接口):通常是一个接口,定义一个执行命令的
execute()
方法。 - ConcreteCommand(具体命令):实现命令接口,并定义执行该命令的具体操作,通常将请求的接收者(Receiver)和操作绑定在一起。
- Client(客户端):创建一个具体命令对象并设置其接收者(Receiver)。
- Invoker(调用者):请求命令的执行。通常在用户操作时,会调用
execute()
方法。 - Receiver(接收者):知道如何实施与执行一个请求相关的操作,实际的业务逻辑通常由此类执行。
案例实现
一个 图形编辑器,用户可以执行对图形的操作(如绘制、擦除等),并能够撤销这些操作。
案例类图
命令调用者类
依赖于命令接口,命令接口下的具体命令类
操作实际的业务逻辑
。
Command接口
public interface Command { void execute(); void undo(); }
绘制图形命令
// ConcreteCommand - 绘制图形命令 public class DrawShapeCommand implements Command { private Shape shape; public DrawShapeCommand(Shape shape) { this.shape = shape; } @Override public void execute() { shape.draw(); } @Override public void undo() { shape.erase(); } } // ConcreteCommand - 删除图形命令 class EraseShapeCommand implements Command { private Shape shape; public EraseShapeCommand(Shape shape) { this.shape = shape; } @Override public void execute() { shape.erase(); } @Override public void undo() { shape.draw(); } }
接受者--抽象图形类
public abstract class Shape { protected String name; public abstract void draw(); public abstract void erase(); }
接受者--具体图形类
public class Circle extends Shape { public Circle() { this.name = "Circle"; } public void draw() { System.out.println("绘制图形 " + name); } public void erase() { System.out.println("擦除图形 " + name); } } class Rectangle extends Shape { public Rectangle() { this.name = "Rectangle"; } public void draw() { System.out.println("绘制图形 " + name); } public void erase() { System.out.println("擦除图形 " + name); } }
命令调用者
public class CommandInvoker { private Stack<Command> commandHistory = new Stack<>(); public void executeCommand(Command command) { command.execute(); commandHistory.push(command); } public void undo() { if (!commandHistory.isEmpty()) { Command lastCommand = commandHistory.pop(); lastCommand.undo(); } else { System.out.println("No commands to undo."); } } }
测试代码
public class CommandDemo { public static void main(String[] args) { // 创建图形 Shape circle = new Circle(); Shape rectangle = new Rectangle(); // 创建命令 Command drawCircleCommand = new DrawShapeCommand(circle); Command drawRectangleCommand = new DrawShapeCommand(rectangle); Command eraseCircleCommand = new EraseShapeCommand(circle); Command eraseRectangleCommand = new EraseShapeCommand(rectangle); // 创建命令调用者 CommandInvoker invoker = new CommandInvoker(); // 执行命令 invoker.executeCommand(drawCircleCommand); // 绘制圆形 invoker.executeCommand(drawRectangleCommand); // 绘制矩形 invoker.executeCommand(eraseCircleCommand); // 删除圆形 invoker.executeCommand(eraseRectangleCommand); // 删除矩形 // 撤销操作 invoker.undo(); // 撤销删除矩形 invoker.undo(); // 撤销删除圆形 invoker.undo(); // 撤销绘制矩形 invoker.undo(); // 撤销绘制圆形 invoker.undo(); } }
测试结果
绘制图形 Circle
绘制图形 Rectangle
擦除图形 Circle
擦除图形 Rectangle
绘制图形 Rectangle
绘制图形 Circle
擦除图形 Rectangle
擦除图形 Circle
No commands to undo.
优缺点和适用场景
优点:
- 解耦请求者和接收者:请求者(客户端)不需要知道接收者的具体实现,只需要知道命令接口。
- 支持撤销操作:可以将命令对象设计为支持撤销的操作,使得某些操作能够撤回。
- 可以将命令参数化:命令可以作为参数传递,或被存储起来,支持批量操作。
- 扩展性好:增加新的命令时,不需要改变现有代码,只需要新增具体命令类。
缺点:
- 增加类的数量:每个具体命令类都需要创建一个类,可能导致类的数量增多。
- 实现复杂度:如果系统中的命令非常多,可能导致命令类实现过于复杂。
命令模式在 GUI 程序、事务管理、队列任务等场景中非常常见。
适用场景
- 需要解耦请求者和接收者。
- 需要撤销、重做操作。
- 需要存储请求、支持队列、日志功能。
- 需要动态选择操作或扩展操作。
- 需要将多个操作封装为一个命令。
- 需要管理跨平台或多设备的操作。
总结
命令模式的核心关注点是将请求封装成对象,从而使得请求的发送者(调用者)和接收者(执行者)解耦。命令模式通过把请求封装成命令对象,使得你可以在不改变请求者的情况下改变请求的执行方式、顺序或者操作对象。
-
行为封装:命令模式将请求、操作或事务封装为命令对象,这些对象可以被请求者调用。请求者不关心具体操作的执行方式,只需要调用命令对象的执行方法即可。
-
请求者和执行者解耦:通过引入命令对象,调用者和被调用者的关系被解耦,调用者不需要知道如何执行操作,也不需要知道具体的操作是什么,只需要发出命令请求。
需要查看往期设计模式文章的,可以在个人主页中或者文章开头的集合中查看,可关注我,持续更新中。。。