概念
命令设计模式
(Command Pattern)是一种行为设计模式,它将请求的操作封装为一个对象,从而实现请求者和执行者之间的解耦。这样,请求者只需要知道如何发送请求,而无需关心请求的具体执行过程。命令模式在很多场景下都非常有用,例如撤销操作、延迟执行、记录操作日志等。
组成角色
- 抽象命令(Command):定义命令的接口,通常包含一个名为
execute()
的方法。 - 具体命令(ConcreteCommand):实现抽象命令接口,封装具体的操作逻辑。
- 请求者(Invoker):负责调用命令对象来执行请求,通常不需要知道命令的具体实现。
- 接收者(Receiver):负责执行具体的操作,通常是一个具体的类或对象。
相关图示
示例代码
// 抽象命令
interface Command {
void execute();
}
// 具体命令
class ConcreteCommand implements Command {
private Receiver receiver;
public ConcreteCommand(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void execute() {
receiver.action();
}
}
// 接收者
class Receiver {
void action() {
System.out.println("执行具体操作");
}
}
// 请求者
class Invoker {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void invoke() {
command.execute();
}
}
// 客户端代码
public class CommandPatternDemo {
public static void main(String[] args) {
//**在这个示例中,我们定义了一个抽象命令Command接口和一个具体命令ConcreteCommand类。具体命令封装了接收者Receiver的操作逻辑。请求者Invoker负责调用命令对象来执行请求。客户端代码创建了接收者、具体命令和请求者,并通过调用请求者的invoke()方法来执行请求。这样,请求者和接收者之间实现了解耦,使得代码更加灵活、易于维护。**/
// 创建接收者
Receiver receiver = new Receiver();
// 创建具体命令
Command command = new ConcreteCommand(receiver);
// 创建请求者
Invoker invoker = new Invoker();
invoker.setCommand(command);
// 执行请求
invoker.invoke();
}
}
框架中的运用
在Dubbo框架中,命令模式被用于处理网络通信中的请求和响应。下面是相关流程中的简版代码
1、org.apache.dubbo.remoting.exchange.ExchangeChannel
接口。这是Dubbo通信层中的一个关键接口,它代表了一个通信通道。其中,request(Object request)
方法用于发送请求,而send(Object message)
方法用于发送响应:
public interface ExchangeChannel extends Channel {
CompletableFuture<Object> request(Object request) throws RemotingException;
CompletableFuture<Object> request(Object request, int timeout) throws RemotingException;
void send(Object message) throws RemotingException;
// 省略部分代码...
}
2、当通道收到一个请求或响应时,它会将其封装为org.apache.dubbo.remoting.exchange.Request
或org.apache.dubbo.remoting.exchange.Response
对象。这两个类分别表示请求和响应,其中Request
类包含请求的数据以及请求的ID,而Response
类包含响应的数据、状态以及请求的ID:
public class Request {
private final long id;
private String version;
private boolean twoWay = true;
private boolean event = false;
private Object data;
// 省略部分代码...
}
public class Response {
private final long id;
private String version;
private int status;
private boolean event = false;
private Object result;
private Throwable exception;
// 省略部分代码...
}
3、在通信层,有一个org.apache.dubbo.remoting.Dispatcher
接口,它负责分发请求和响应。分发器会将收到的请求和响应封装为ChannelHandler
对象,并调用相应的方法进行处理。例如,当分发器收到一个请求时,它会调用ChannelHandler
的received(Channel channel, Object message)
方法进行处理:
public interface ChannelHandler {
void connected(Channel channel) throws RemotingException;
void disconnected(Channel channel) throws RemotingException;
void sent(Channel channel, Object message) throws RemotingException;
void received(Channel channel, Object message) throws RemotingException;
void caught(Channel channel, Throwable exception) throws RemotingException;
}
4、对于请求的处理,通常会使用org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler
类。这个类实现了ChannelHandler
接口,并重写了received(Channel channel, Object message)
方法。在这个方法中,它首先判断收到的消息是否为Request
对象,然后根据请求的类型(如普通请求、心跳请求等)进行相应的处理:
public class HeaderExchangeHandler implements ChannelHandlerDelegate {
//展示相关代码
@Override
public void received(Channel channel, Object message) throws RemotingException {
if (message instanceof Request) {
//省略部分代码
// 处理请求
handleRequest(channel, (Request) message);
} else if (message instanceof Response) {
// 处理响应
handleResponse(channel, (Response) message);
} else {
//省略部分代码
// 对于其他类型的消息,调用父类的方法进行处理
handler.received(channel, message);
}
}
}
适用场景
- 请求调用者与请求接收者需要解耦:命令模式将调用操作的对象与知道如何实现这些操作的对象分离开来,这有助于降低系统各部分之间的耦合。
- 需要抽象出待执行的操作以参数化其他对象:命令模式可以将请求封装为具体的命令对象,这些对象可以在运行时进行传递、组合和替换,实现了更高程度的灵活性。
- 需要支持撤销操作:命令模式可以在命令接口中添加一个名为
undo
的方法,以便在需要时撤销操作。具体命令类可以保存先前的状态,以便在执行undo
方法时能够恢复原始状态。 - 需要支持宏命令:命令模式允许将多个命令组合成一个宏命令对象,即一个命令序列。这个宏命令对象可以像单个命令一样被执行,从而实现对一组命令的统一管理。
- 需要将命令放入队列中进行排队执行:命令模式可以将命令对象放入队列中,然后逐个执行这些命令。这可以帮助实现后台任务队列、工作线程池等功能。
- 需要记录命令日志:命令模式可以用于记录命令的详细信息,这有助于实现日志记录、审计、事务等功能。