掌握Java命令模式:将请求封装成对象,解耦发送者和接收者的关系
文章目录
一、简介
1.1 介绍命令模式
命令模式是一种行为设计模式,它将请求封装成对象,以便可以使用不同的请求、队列或日志来参数化其他对象。命令模式允许请求的发出者和接收者之间解耦,让不同的对象之间更加灵活和独立地交互。
使用命令模式,我们可以将请求与发送请求的对象解耦。具体来说,我们可以创建一个命令接口,该接口要求执行某些操作的所有具体命令对象实现该接口。然后,我们可以使用具体的命令对象来代表不同的请求,并将其传递给其他对象执行。
1.2 它的优点和使用场景
命令模式的优点包括:
- 解耦发送者和接收者:通过命令对象将请求发送给接收者,可以避免发送者和接收者之间的直接耦合。
- 容易增加新命令:每个具体命令就像是独立的一部分,因此很容易添加新的具体命令,从而扩展系统功能。
- 支持撤销操作:通过保存历史命令记录,命令模式可以支持撤销操作。
命令模式的使用场景包括:
- 需要将请求和接收者解耦的场景。
- 需要支持批处理、撤销和重做等操作的场景。
- 需要生成日志或记录请求历史的场景。
二、命令模式的工作原理
2.1 定义命令模式
命令模式是一种对象行为型模式,它的作用是将请求封装为一个对象,从而使我们可以用不同的请求来对客户端进行参数化,对请求排队或者记录请求日志,以及支持可撤销和恢复的操作。
2.2 命令模式的角色
- 抽象命令(Command):定义命令的接口,包含执行命令的execute()方法。
- 具体命令(ConcreteCommand):实现抽象命令接口,通过调用接收者来执行具体的操作。
- 接收者(Receiver):执行具体操作的对象。
- 调用者(Invoker):负责调用命令对象并执行请求。
- 客户端(Client):创建命令对象,并将命令对象交给调用者进行调用执行。
2.3 命令模式的执行流程
- 客户端创建具体命令对象,指定其接收者。
- 创建调用者对象,并将具体命令对象初始化到调用者中。
- 调用者的调用方法会执行具体命令对象的execute()方法。
- 具体命令对象调用接收者的相关方法来执行具体操作。
2.4 命令模式的关键要素
- 命令对象将请求封装成对象,解除请求发送者和接收者之间的紧密耦合关系。
- 命令对象包含了接收者对象的一个引用,避免了发送者和接收者之间的直接关系。
- 接收者对象知道如何实现真正的操作。
- 调用者对象判断执行哪个具体的命令对象来进行操作,提供了一种灵活的场景。
三、实现Java命令模式
3.1 创建命令接口
首先,我们需要定义一个命令接口(Command),该接口包含一个执行命令的execute()方法,该方法实现了具体命令的操作。
public interface Command {
void execute();
}
3.2 实现具体命令类
接下来,我们需要实现具体命令类(ConcreteCommand),该类实现命令接口,并负责调用接收者对象执行具体操作。
public class ConcreteCommand implements Command {
private Receiver receiver;
public ConcreteCommand(Receiver receiver) {
this.receiver = receiver;
}
public void execute() {
receiver.action();
}
}
3.3 创建请求者类Invoker
然后创建调用者对象(Invoker),该对象负责调用具体命令对象,并执行相应的请求。
public class Invoker {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void executeCommand() {
command.execute();
}
}
3.4 创建接收者类Receiver
接收者对象(Receiver)包含了实际执行操作的方法。
public class Receiver {
public void action() {
System.out.println("Receiver: Performing action");
}
}
3.5 创建客户端类Client
需要创建一个客户端类(Client),它负责创建具体命令对象、接收者对象和调用者对象,并将它们协调起来执行命令。
public class Client {
public static void main(String args[]) {
Receiver receiver = new Receiver();
Command command = new ConcreteCommand(receiver);
Invoker invoker = new Invoker();
invoker.setCommand(command);
invoker.executeCommand();
}
}
四、命令模式扩展
4.1 反悔命令 Undo Command
反悔命令是一种特殊的命令,它允许用户撤销最近执行的操作。为了实现反悔功能,我们需要在每个具体命令类中实现一个反悔方法,该方法应该能够撤销相应的命令操作。一种常见的实现方式是使用一个栈来维护所有执行的命令对象,当用户请求撤销操作时,我们可以从栈中弹出最后一个对象并调用它的撤销方法。
4.2 宏命令 Macro Command
宏命令是一种将多个命令组合在一起的命令模式。由于宏命令本身也是一种命令对象,因此它可以被其他命令对象传递和执行。一个宏命令中包含了多个具体命令对象,并负责协调它们的执行。例如,当用户单击一个复杂的按钮时,可以将多个具体命令对象组合在一起形成一个宏命令,并将宏命令对象传递给调用者进行执行。
4.3 多个接收者的命令 Multiple Receivers Command
在标准的命令模式中,一个具体命令对象只能绑定一个接收者对象,因此无法直接将命令发送给多个接收者对象。如果想要实现一个将命令发送给多个接收者的功能,可以使用一种称为“回调”的方法。回调是一种程序设计技术,它允许我们将一段代码传递给另一个对象,并将该代码作为参数在接收方触发。在命令模式中,我们可以实现一个多重命令接口(MultipleCommand),该接口中包含了回调方法,具体命令对象实现该接口,并在回调方法中调用接收者对象执行相应的操作。这样,命令对象就可以将命令发送到多个接收者对象中了。
五、在Java项目中使用命令模式
5.1 例子1:使用命令模式实现日志记录
- 首先定义命令接口,并为每个具体命令对象实现日志记录的方法:
public interface Command {
void execute();
void log();
}
public class LoginCommand implements Command {
private LoginReceiver receiver;
public LoginCommand(LoginReceiver receiver) {
this.receiver = receiver;
}
public void execute() {
receiver.login();
}
public void log() {
System.out.println("LoginCommand: Logged user login");
}
}
public class LogoutCommand implements Command {
private LogoutReceiver receiver;
public LogoutCommand(LogoutReceiver receiver) {
this.receiver = receiver;
}
public void execute() {
receiver.logout();
}
public void log() {
System.out.println("LogoutCommand: Logged user logout");
}
}
- 然后创建接收者对象,并实现具体操作:
public interface LoginReceiver {
void login();
}
public class UserLoginReceiver implements LoginReceiver {
public void login() {
System.out.println("UserLoginReceiver: User logged in");
}
}
public interface LogoutReceiver {
void logout();
}
public class UserLogoutReceiver implements LogoutReceiver {
public void logout() {
System.out.println("UserLogoutReceiver: User logged out");
}
}
- 创建一个调用者对象,负责执行具体的命令并记录日志:
public class CommandInvoker {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void execute() {
command.execute();
command.log();
}
}
- 在客户端代码中,创建具体命令对象和相关的接收者,并将它们传递给调用者执行:
public static void main(String[] args) {
LoginReceiver loginReceiver = new UserLoginReceiver();
Command loginCommand = new LoginCommand(loginReceiver);
LogoutReceiver logoutReceiver = new UserLogoutReceiver();
Command logoutCommand = new LogoutCommand(logoutReceiver);
CommandInvoker invoker = new CommandInvoker();
// Execute login command
invoker.setCommand(loginCommand);
invoker.execute();
// Execute logout command
invoker.setCommand(logoutCommand);
invoker.execute();
}
- 运行程序,观察控制台输出:
UserLoginReceiver: User logged in
LoginCommand: Logged user login
UserLogoutReceiver: User logged out
LogoutCommand: Logged user logout
可以看到,通过使用命令模式,我们可以将具体操作和日志记录分离开来,避免了收到操作代码的干扰,同时也更方便地记录了操作历史。
相关文章
- Java面向对象高级--抽象类与接口的应用
- 介绍几个好用的Java库
- [转载]Eclipse提示No java virtual machine
- Java实现 LeetCode 692 前K个高频单词(map的应用)
- Java实现 LeetCode 664 奇怪的打印机(DFS)
- Java实现 LeetCode 518 零钱兑换 II
- Java实现 LeetCode 149 直线上最多的点数
- Java实现第八届蓝桥杯青蛙跳杯子
- Java异常封装(自己定义错误码和描述,附源码)
- 【java设计模式】之 工厂(Factory)模式
- Atitit usbQb212 oo 面向对象封装的标准化与规范解决方案java c# php js
- Atitit. C# java 的api 目录封装结构映射总结
- 常见Java面试题之静态变量和实例变量的区别
- java 封装 总结
- java使用BeanUtils封装file类型表单数据到一个对象中
- Exception in thread "main" java.lang.NullPointerException
- Flex+Java+Blazeds
- JAVA面试中遇到的那些坑,80%的人都种过招!
- java Intellij IDEA设置中文
- Java日志 - log4简单使用实例及其再次封装