命令模式是一种行为型设计模式,核心是将每种请求或操作封装为一个独立的对象,从而可以集中管理这些请求或操作,比如将请求队列化依次执行、或者对操作进行记录和撤销。
命令模式通过将请求的发送者(客户端)和接收者(执行请求的对象)解耦,提供了更大的灵活性和可维护性。
听不懂上面这句话很正常,我来举个例子。
我们在生活中都用过电视机,我们
就相当于客户端,要操作电视来换台;而电视
就是执行请求的对象,要根据我们的操作来换台。但是我们一般不会直接按电视上的按钮来换台,而是用一个遥控器
,通过点击遥控器上的操作按钮
来控制电视。
这样就相当于把我们和电视解耦了。哪怕遥控器丢了,再换一个遥控器就好了;而且现在手机都能作为万能的电视遥控器,我们可以同时遥控多个品牌的设备,不用关心设备的具体品牌型号,提供了更大的方便。
正如上面的例子,命令模式最大的优点就是解耦请求发送者和接受者,让系统更加灵活、可扩展。
由于每个操作都是一个独立的命令类,所以我们需要新增命令操作时,不需要改动现有代码。
命令模式典型的应用场景:
通过上面用户使用遥控器来操作电视机设备的例子,带大家理解命令模式的关键要素和实现代码。
相当于遥控器操作按钮的制作规范
命令是一个抽象类或接口,它定义了执行操作的方法,通常是execute
,该方法封装了具体的操作。
代码如下:
public?interface?Command?{
????void?execute();
}
相当于遥控器的某个操作按钮
具体命令是命令接口的具体实现类,它负责将请求传递给接收者(设备)并执行具体的操作。
比如定义一个关闭设备命令,代码如下:
public?class?TurnOffCommand?implements?Command?{
????private?Device?device;
????public?TurnOffCommand(Device?device)?{
????????this.device?=?device;
????}
????public?void?execute()?{
????????device.turnOff();
????}
}
还可以定义开启设备命令,代码如下:
public?class?TurnOnCommand?implements?Command?{
????private?Device?device;
????public?TurnOnCommand(Device?device)?{
????????this.device?=?device;
????}
????public?void?execute()?{
????????device.turnOn();
????}
}
相当于被遥控的设备
接收者是最终执行命令的对象,知道如何执行具体的操作。
比如定义一个设备类,代码如下:
public?class?Device?{
????private?String?name;
????public?Device(String?name)?{
????????this.name?=?name;
????}
????public?void?turnOn()?{
????????System.out.println(name?+?"?设备打开");
????}
????public?void?turnOff()?{
????????System.out.println(name?+?"?设备关闭");
????}
}
相当于遥控器
作用是接受客户端的命令并执行。
比如定义遥控器类,代码如下:
public?class?RemoteControl?{
????private?Command?command;
????public?void?setCommand(Command?command)?{
????????this.command?=?command;
????}
????public?void?pressButton()?{
????????command.execute();
????}
}
以上只是最基础的调用者类,还可以给遥控器类增加更多能力,比如存储历史记录、撤销重做等。
相当于使用遥控器的人
客户端的作用是创建命令对象并将其与接收者关联(绑定设备),然后将命令对象传递给调用者(按遥控器),从而触发执行。
示例客户端代码如下:
public?class?Client?{
????public?static?void?main(String[]?args)?{
????????//?创建接收者对象
????????Device?tv?=?new?Device("TV");
????????Device?stereo?=?new?Device("Stereo");
????????//?创建具体命令对象,可以绑定不同设备
????????TurnOnCommand?turnOn?=?new?TurnOnCommand(tv);
????????TurnOffCommand?turnOff?=?new?TurnOffCommand(stereo);
????????//?创建调用者
????????RemoteControl?remote?=?new?RemoteControl();
????????//?执行命令
????????remote.setCommand(turnOn);
????????remote.pressButton();
????????remote.setCommand(turnOff);
????????remote.pressButton();
????}
}
在这个示例中,命令模式将遥控器按钮的按下操作与实际设备的开关操作解耦,从而实现了灵活的控制和可扩展性。
整个程序的 UML 类图如下:
鱼皮独立开发的 yuindex 网页终端项目,就是以命令模式为核心实现的,前后端同学都可以学习:https://github.com/liyupi/yuindex
编程导航星球的定制化代码生成项目就是使用了命令模式来开发命令行应用。