命令模式
-
- 一.简介
- [二. 案例](#二. 案例)
-
- [2.1 接收者(Receiver)](#2.1 接收者(Receiver))
- [2.2 命令接口实现对象(ConcreteCommand)](#2.2 命令接口实现对象(ConcreteCommand))
- [2.3 调用者( invoker)](#2.3 调用者( invoker))
- [2.4 获取Receiver对象](#2.4 获取Receiver对象)
- [2. 5 装配者客户端测试](#2. 5 装配者客户端测试)
- [三. 结论](#三. 结论)
-
- [3.1 要点](#3.1 要点)
- [3.2 示例](#3.2 示例)
前言
本设计模式专栏写了很多设计模式的文章,希望大家点赞收藏一起学习
这是我在这个网站整理的笔记,有错误的地方请指出,关注我,接下来还会持续更新。
作者:神的孩子都在歌唱
一.简介
百度百科: 在软件系统中,行为请求者 与行为实现者 通常呈现一种紧耦合 。但在某些场合,比如要对行为进行记录、撤销/重做、事务 等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将行为请求者与行为实现者解耦?将一组行为抽象为对象 ,实现二者之间的松耦合 。这就是命令模式(Command Pattern)。
个人理解: 某个行为 有多个对象执行,并且我们需要对这个行为 进行一些监控等处理 。那么我们就需要将这种行为抽象 出来,这种就叫做命令模式。
在命令模式中有如下角色:
- 接收者(Receiver): 任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。
- 命令接口实现对象(ConcreteCommand): 是"虚"的实现;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。
- 调用者(Invoker): 要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。
- 装配者(Client): 创建具体的命令对象,并且设置命令对象的接收者。注意这个不是我们常规意义上的客户端,而是在组装命令对象和接收者。
接下来使用示例和代码来加深理解
二. 案例
举个例子:
- 场景:有两个人听从教练的指挥互相传球,一个用左手传,一个用右手传。
- 结论: 可以看出,我们有两个对象,简称为左手人和右手人 ,他们有一个共同的行为 就是传球 。由谁进行传球这个行为是教练下的命令。这时候就可以使用命令模式。
类图如下
2.1 接收者(Receiver)
我们首先定义两个人物对象
定义接收者的接口
java
/**
* @Author chenyunzhi
* @DATE 2024/9/12 15:13
* @Description: 接收者
*/
public interface BallReceiver {
void passBall();
}
使用左手投球的人
java
/**
* @Author chenyunzhi
* @DATE 2024/9/12 15:14
* @Description: 使用左手接收者
*/
public class LeftHandPeopleReceiver implements BallReceiver{
@Override
public void passBall() {
System.out.println("人物1使用左手传球");
}
}
使用右手投球的人
java
/**
* @Author chenyunzhi
* @DATE 2024/9/12 15:16
* @Description: 使用右手接收者
*/
public class RightHandPeopleReceiver implements BallReceiver{
@Override
public void passBall() {
System.out.println("人物2使用右手传球");
}
}
2.2 命令接口实现对象(ConcreteCommand)
接下来我们要将传球这个命令抽象出来
定义执行命令的接口
java
/**
* @Author chenyunzhi
* @DATE 2024/9/12 15:19
* @Description: 执行命令
*/
public interface Command {
void execute();
}
定义传球命令
java
/**
* @Author chenyunzhi
* @DATE 2024/9/12 15:23
* @Description: 传球命令
*/
public class PassBallCommand implements Command{
private final BallReceiver ballReceiver;
public PassBallCommand(BallReceiver ballReceiver) {
this.ballReceiver = ballReceiver;
}
@Override
public void execute() {
this.ballReceiver.passBall();
}
}
现在我们已经准备好了接收者和命令实现,因此我们可以开始实现 调用者( invoker) 类。
2.3 调用者( invoker)
调用者的任务就是负责调用具体的命令
java
/**
* @Author chenyunzhi
* @DATE 2024/9/12 16:27
* @Description: 命令的调度者,执行命令
*/
public class BallInvoker {
public Command command;
public BallInvoker(Command command) {
this.command = command;
}
public void execute() {
this.command.execute();
}
}
2.4 获取Receiver对象
命令模式已经准备就绪,我们可以开始编写一个简单的命令模式客户端程序。但在此之前,我将提供一个方法来创建相应的 BallReceiver
对象。我们可以通过工厂+策略模式去获取对应的人物对象
java
/**
* @Author chenyunzhi
* @DATE 2024/9/12 16:36
* @Description: 使用工厂模式设计
*/
public class PeopleReceiverFactory {
public static BallReceiver getBallReceiver(String people) {
switch (people) {
case "LEFT_HAND":
return new LeftHandPeopleReceiver();
case "RIGHT_HAND":
return new RightHandPeopleReceiver();
default:
return null;
}
}
}
如果命令很多,比如这里不止传球,还有打球,投球等等,这些行为可以通过工厂的方式获取。
2. 5 装配者客户端测试
准备工作已经完成,那么我们接下来就也可以开始测试了
java
/**
* @Author chenyunzhi
* @DATE 2024/9/12 16:45
* @Description:
*/
public class CommandPatternClient {
/**
* 装配者
*/
public static void ballClient(String receiver) {
// 创建左手传球的接收者对象
BallReceiver ballReceiver = PeopleReceiverFactory.getBallReceiver(receiver);
// 创建命令并与接收者关联
PassBallCommand passBallCommand = new PassBallCommand(ballReceiver);
// 创建调用者与命令关联
BallInvoker ballInvoker = new BallInvoker(passBallCommand);
// 对调用者对象执行命令
ballInvoker.execute();
}
public static void main(String[] args) {
ballClient("LEFT_HAND");
ballClient("RIGHT_HAND");
}
}
ballClient方法负责组装命令对象和接收者。最后输出结果
三. 结论
3.1 要点
- 接收器实现与命令实现是分开的。
- 命令实现类选择要在接收器对象上调用的方法,接收器中的每个方法都将有一个命令实现。它充当接收者和操作方法之间的桥梁。
- Invoker 类只是将请求从 client 转发到 command 对象。
- 客户端负责实例化适当的命令和接收器实现,然后将它们关联在一起。
- Client 还负责实例化 invoker 对象并将 command object 与其关联,并执行 action 方法。
- 命令设计模式很容易扩展,我们可以在接收器中添加新的动作方法,并在不改变客户端代码的情况下创建新的命令实现。
- Command 设计模式的缺点是,由于有太多的关联,代码会变得庞大且令人困惑。
3.2 示例
java.lang.Runnable
javax.swing.Action
作者:神的孩子都在歌唱
本人博客:https://blog.csdn.net/weixin_46654114
转载说明:务必注明来源,附带本人博客连接。