在上一篇文章中,大概总结了单例模式,今天我要说的是另外一种模式即命令模式。废话不多说,直接进入主题。命令模式的定义:将 "请求" 封装成对象,以便使用不同的请求,队列或者日志来参数化其他对象。命令模式也支持可撤销操作。
一看到这个定义是不是感觉有点懵圈,那么我们通过下面的例子来看看命令模式到底是怎么回事呢?
命令模式是对命令的封装,它把发出命令的责任(请求者)和执行命令的责任(接收者)分割开,委派给不同的对象。这就是把请求者和接收者解耦合。请求者只负责发起命令,它不需要知道接收者是如何去接收命令,也不需要知道接收者是如何具体的执行命令的。所以命令模式分为四个角色:
命令角色(Command): 声明了一个给所有具体命令类的抽象接口。
具体命令角色(ConcreteCommand):定义一个接收者和行为之间的弱耦合;实现 execute() 方法,负责调用接收者的相应操作。execute() 方法通常叫做执行方法。
请求者角色(Invoker):负责调用命令对象执行请求,相关的方法叫做行动方法。
接收者角色(Receiver):负责具体实施和执行一个请求。任何一个类都可以成为接收者,实施和执行请求的方法叫做行动方法。
命令模式的结构可以用下面的图来示意:
这个图中说明了上面的四种角色是如何联系在一起的。下面我用代码来说明一下这几个角色是如何在命令模式中工作的。
接收者角色类:
- public class Receiver {
- /** * 真正执行命令相应的操作 */
- public void action() {
- System.out.println("执行操作");
- }
- }
抽象命令角色类:
- public interface Command {
- /** * 执行方法 */
- void execute();
- }
具体命令角色类:
- public class ConcreteCommand implements Command { //持有相应的接收者对象 private Receiver receiver = null; /** * 构造方法 */ public ConcreteCommand(Receiver receiver){ this.receiver = receiver; } @Override public void execute() { //通常会转调接收者对象的相应方法,让接收者来真正执行功能 receiver.action(); }}
请求者角色类:
- public class Invoker {
- /** * 持有命令对象 */
- private Command command = null;
- /** * 构造方法 * 这里是构造方法传入命令,也可以单独写个set方法来设置command。 */
- public Invoker(Command command) {
- this.command = command;
- }
- /** * 行动方法 */
- public void action() {
- command.execute();
- }
- }
下面我们有了上面的类,我们可以在来合理的使用命令模式了,如下:
- public class Client {
- public static void main(String[] args) { //创建接收者 Receiver receiver = new Receiver(); //创建命令对象,设定它的接收者 Command command = new ConcreteCommand(receiver); //创建请求者,把命令对象设置进去 Invoker invoker = new Invoker(command); //执行方法 invoker.action(); }}
针对上面的模式,我们举个具体的例子其中使用到命令模式。
遥控器(RemoteControl)
电视的遥控器本身有很多按钮,我们假设每个按钮就是一个命令,这样当我们按下一个按钮的时候其实就是再请求一个操作,然后由遥控器接收到命令并去对电视做相应的反应,我们不需要知道遥控器具体做了什么而让电视做相应的反应,我们只负责传达我们想要的命令给遥控器就可以了。
下面我们用代码先来实现遥控器上的两个功能:电源的开关,音量大小。
接收者角色,由遥控器扮演
- public class RemoteControl {
- public void turnOn() {
- System.out.println("开机");
- }
- public void turnOff() {
- System.out.println("关机");
- }
- public void soundOn() {
- System.out.println("取消静音");
- }
- public void soundOff() {
- System.out.println("静音");
- }
- }
抽象命令角色类
- public interface Command {
- /** * 执行方法 */
- public void execute();
- }
具体命令角色类
- public class PowerOnCommand implements Command {
- private RemoteControl mRemoteControl;
- public PowerOnCommand(RemoteControl control) {
- mRemoteControl = control;
- }@Override public void execute() {
- mRemoteControl.turnOn();
- }
- }
- public class PowerOffCommand implements Command {
- private RemoteControl mRemoteControl;
- public PowerOffCommand(RemoteControl control) {
- mRemoteControl = control;
- }@Override public void execute() {
- mRemoteControl.turnOff();
- }
- }
- public class SoundOnCommand implements Command {
- private RemoteControl mRemoteControl;
- public SoundOnCommand(RemoteControl control) {
- mRemoteControl = control;
- }@Override public void execute() {
- mRemoteControl.soundOn();
- }
- }
- public class SoundOffCommand implements Command {
- private RemoteControl mRemoteControl;
- public SoundOffCommand(RemoteControl control) {
- mRemoteControl = control;
- }@Override public void execute() {
- mRemoteControl.soundOff();
- }
- }
请求者角色,由键盘类扮演
- public class Keyboard { // 开机 private Command powerOn; // 关机 private Command powerOff; // 音量增 private Command soundOn; // 音量减 private Command soundOff; public void setPowerOn(Command powerOn) { this.powerOn = powerOn; } public void setPowerOff(Command powerOff) { this.powerOff= powerOff; } public void setSoundOn(Command soundOn) { this.soundOn= soundOn; } public void setSoundOff(Command soundOff) { this.soundOff = soundOff; } // 按下开机按钮 public void turnOn(){ powerOn.execute(); } // 按下关机按钮 public void turnOff(){ powerOff.execute(); } // 按下音量增按钮 public void soundOn(){ soundOn.execute(); } // 按下音量减按钮 public void soundOff(){ soundOff.execute(); }}
现在小红可以拿遥控器操作电视了。代码如下:
- public class XiaoHong {
- public static void main(String[] args) { // 创建接收者 RemoteControl rc = new RemoteControl(); // 创建命令对象 Command powerOnCommand = new PowerOnCommand(rc); Command powerOffCommand = new PowerOffCommand(rc); Command soundOnCommand = new SoundOnCommand(rc); Command soundOffCommand = new SoundOffCommand(rc); // 创建请求者 Keyboard keyboard = new Keyboard(); keyboard.setPowerOn(powerOnCommand); keyboard.setPowerOff(powerOffCommand); keyboard.setSoundOn(soundOnCommand); keyboard.setSoundOff(soundOffCommand); // 测试 keyboard.turnOn(); keyboard.turnOff(); keyboard.soundOn(); keyboard.soundOff(); }}
这样我们就完成了对命令模式的使用。大家是不是对命令模式又有了更进一步的认识了呢?
大家想一想,我们上面都是按个按钮单个操作来执行的,那如果我们按一个按钮能不能执行一组操作呢?答案当然是可以的,而且这种实现方式就叫做宏命令。下面我们来看一下具体的实现。
宏命令
首先我们来定义一个宏命令的接口,该接口有两个方法:remove 和 add 方法,并且这个接口实现了抽象命令接口(Command)
- public interface MacroCommand extends Command {
- /** * 宏命令聚集的管理方法 * 可以添加一个成员命令 */
- public void add(Command cmd);
- /** * 宏命令聚集的管理方法 * 可以删除一个成员命令 */
- public void remove(Command cmd);
- }
具体的宏命令 MacroRemoteControlCommand 类负责把个别的命令合成宏命令。
- public class MacroRemoteControlCommand implements MacroCommand {
- private ListcommandList = new ArrayList();
- /** * 宏命令聚集管理方法 */
- @Override public void add(Command cmd) {
- commandList.add(cmd);
- }
- /** * 宏命令聚集管理方法 */
- @Override public void remove(Command cmd) {
- commandList.remove(cmd);
- }
- /** * 执行方法 */
- @Override public void execute() {
- for (Command cmd: commandList) {
- cmd.execute();
- }
- }
- }
下面我们假设有一个按钮,先是把音量调大,然后再把音量调小。当然这个按钮实际不存在(哈),我们只是为了测试做个假设。下面我们看怎么去实现一个按钮执行一组操作呢?
- public class XiaoHong {
- public static void main(String[] args) { // 创建接收者 RemoteControl rc = new RemoteControl(); // 创建命令对象 Command soundOnCommand = new SoundOnCommand(rc); Command soundOffCommand = new SoundOffCommand(rc); // 创建宏命令 MacroCommand mc = new MacroRemoteControlCommand(); mc.add(soundOnCommand); mc.add(soundOffCommand); mc.execute(); }}
以上就是命令模式中宏命令的使用。那么到这里相信大家都对命令模式有了初步的认识,那么大家想一想在开发中有没有遇到过命令模式的例子呢?其实有一个就是线程池队列。每个新线程任务当做是一个命令对象放到队列中,然后线程池从队列中取出一个任务执行后,并销毁掉该命令对象。不知道大家还有没有想到别的可使用到命令模式的地方。
综合以上,总结一下命令模式的优点:
(1)命令模式使请求的发起者和接收者完全解耦
(2)命令模式把请求封装起来,可以动态地对它进行参数化、队列化和日志化等操作,从而使得系统更灵活。
(3)命令模式中的命令对象能够很容易地组合成复合命令,也就是宏命令,从而使系统操作更简单,功能更强大。
(4)由于发起命令的对象和具体的实现完全解耦,因此扩展新的命令就很容易,只需要实现新的命令对象,然后在装配的时候,把具体的实现对象设置到命令对象中,然后就可以使用这个命令对象,已有的实现完全不用变化。
到这里就对命令模式做了个大概的分析讲解。
来源: http://www.92to.com/bangong/2016/12-03/13846175.html