目录
一、生活中的命令模式:遥控器的启示
不知道大家有没有过这样的体验,忙碌一天回到家,往沙发上一躺,只想舒舒服服看会儿电视。这时候,你无需起身走到电视机前,只需轻轻拿起身边的遥控器,按下对应的按钮,就能随心切换频道、调节音量。这看似平常的操作背后,其实就蕴含着命令模式的思想。
在这个场景里,遥控器就是命令发送者 ,它负责接收我们的操作指令,比如按下 “开 / 关” 按钮、“频道 +”“频道 -” 按钮等。而电视机则是接收者 ,它会根据遥控器传来的指令执行相应的操作,完成开机、关机、切换频道等动作。每一个按钮的操作,比如打开、关闭、切换频道,都可以看作是一个具体的命令 。我们不需要了解电视机内部是如何实现这些功能的,只需要通过遥控器发送命令即可。这就好比我们在餐厅点餐,我们告诉服务员想吃什么,服务员把订单传达给厨房,我们不用关心厨房是如何烹饪的,最终就能享受到美味的菜肴。
从生活中的这个小例子可以看出,命令模式的核心就是将请求封装成对象,把发出请求的责任和执行请求的责任分割开来,使得两者之间通过命令对象进行沟通 。这样做有很多好处,一方面,发送者和接收者解耦,它们不需要了解彼此的具体实现,只需要关注命令的传递和执行;另一方面,方便对命令进行管理和扩展,比如我们可以轻松地增加新的命令,或者修改现有命令的行为,而不会影响到其他部分的代码。接下来,就让我们深入代码的世界,看看命令模式在编程中是如何实现的。
二、命令模式的正式定义与结构
(一)定义剖析
在计算机编程的世界里,命令模式是一种行为设计模式 ,它的定义是:将一个请求封装为一个对象,从而使我们可以用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。简单来说,命令模式就是把 “做什么” 和 “谁来做” 分离开来,让发出请求的对象(发送者)和执行请求的对象(接收者)之间没有直接的依赖关系,它们通过命令对象来进行交互 。
比如在游戏开发中,玩家的各种操作,如移动角色、释放技能、拾取物品等,都可以看作是一个个请求。如果没有命令模式,游戏中的控制类可能需要直接调用角色类的相应方法来实现这些操作,这样控制类和角色类就紧密耦合在一起了。一旦角色类的实现发生变化,控制类也可能需要随之修改。而使用命令模式,我们可以将每个操作封装成一个命令对象,控制类只需要和命令对象打交道,不需要关心具体的操作是如何在角色类中实现的,这就大大提高了代码的可维护性和可扩展性 。
(二)结构成员介绍
命令模式主要包含以下五个角色,它们相互协作,共同实现了请求的封装与解耦 :
-
命令接口(Command):这是一个抽象的接口,它定义了执行操作的统一方法,一般命名为 execute () 。这个接口就像是一个契约,规定了所有具体命令类必须实现的行为。例如,在一个绘图程序中,绘制直线、绘制矩形、绘制圆形等操作都可以抽象出一个共同的命令接口,其中的 execute 方法负责执行相应的绘制操作 。
-
具体命令类(ConcreteCommand):具体命令类实现了命令接口,它持有接收者对象的引用,并在 execute 方法中调用接收者的具体方法来完成实际的操作。每个具体命令类对应一个特定的请求,比如在前面提到的绘图程序中,绘制直线命令类会持有一个图形绘制工具(接收者)的引用,在 execute 方法中调用图形绘制工具的绘制直线方法 。
-
接收者(Receiver):接收者是真正执行命令的对象,它包含了具体的业务逻辑。接收者并不知道自己是被命令对象调用的,它只专注于实现自身的功能。比如在一个文件操作的场景中,文件系统就是接收者,它提供了读取文件、写入文件、删除文件等方法,而具体的命令类(如读取文件命令类、写入文件命令类)会调用文件系统的这些方法来完成相应的操作 。
-
调用者(Invoker):调用者负责调用命令对象的 execute 方法来发出请求,它持有一个或多个命令对象。调用者不需要知道命令的具体实现细节,也不需要知道接收者是谁,它只关心如何触发命令的执行。例如在一个自动化测试框架中,测试用例的执行器就是调用者,它根据配置的测试用例,调用相应的命令对象来执行测试操作 。
-
客户端(Client):客户端负责创建具体命令对象,并设置命令对象的接收者,然后将命令对象传递给调用者。客户端是命令模式的使用者,它了解整个业务场景,知道在什么情况下需要创建哪些命令对象。比如在一个电商系统中,下单操作的客户端代码会创建下单命令对象,将订单处理服务(接收者)设置给下单命令对象,然后将下单命令对象传递给订单处理调用者 。
三、命令模式的代码实现
(一)场景设定
为了更直观地理解命令模式在代码中的实现,我们以游戏开发中的操作控制为例 。假设我们正在开发一款简单的角色扮演游戏,游戏中有玩家可以进行的各种操作,比如打开游戏进入精彩的冒险世界、玩累了关闭游戏休息一下、在遇到怪物时发起攻击等。接下来,我们就逐步用代码实现这些操作背后的命令模式 。
(二)代码逐步构建
定义命令接口:首先,我们需要定义一个命令接口,它是所有具体命令类的抽象,规定了命令执行的统一方法。在 Java 中,代码如下:
// 命令接口
public interface Command {
// 执行命令的方法
void execute();
}
这个Command接口只有一个execute方法,所有具体的命令类都必须实现这个方法,以完成各自特定的操作 。
创建具体命令类:接下来,我们创建具体的命令类,分别对应打开游戏、关闭游戏和攻击怪物的操作。以打开游戏命令类OpenGameCommand为例,代码如下:
// 打开游戏命令类
public class OpenGameCommand implements Command {
// 持有游戏对象的引用
private Game game;
// 构造函数,传入游戏对象
pu