设计模式——命令模式

215 阅读4分钟

一、概述

命令模式(Command模式),它是将一个请求封装为一个对象,从而使得可以用不同的请求对客户进行参数化。这个定义不好理解,举个具体的例子,平时看电视的时候需要用到遥控器,当我们按下遥控器的时候其实就是在发命令,比如切换频道、调音量、开关机等等请求操作,那么我们就把这些请求操作封装成一个对象,客户端接收到哪个参数对象,就执行哪种操作,当然具体的操作还是在电视机中实现的。这样就使得命令请求发送者与操作实现者进行解耦,命令模式也是一种行为型模式。

命令模式是对命令的封装,每一个命令都是一个请求对象,它包含了4个关键的角色:

  • 抽象命令角色:高度抽象命令,包含命令执行的抽象方法。
  • 具体命令角色:一个具体的命令请求,实现了抽象命令角色接口,在具体的抽象方法中实现自己的请求逻辑。
  • 命令接收者角色:接收者在接收到具体的命令后,执行相应的操作,这里接收者就是电视机。
  • 命令请求者角色:负责调用命令对象执行请求,这里请求者就是遥控器。

接下来我们用代码来做说明,这样会更容易了解命令模式。

二、使用

首先我们创建抽象的命令角色,它是所有命令的高度抽象,并有一个发送命令请求的抽象方法。

/**
 * 抽象命令角色
 */
public interface Command {
    void excute();
}

然后是具体的命令角色,所有的具体命令都实现了抽象命令接口,并在抽象方法中实现了自己的逻辑。

/**
 * 具体的命令角色——换台
 */
public class ChangeChannelCommand implements Command {

    private static final String TAG = "XXX";
    private TV tv;

    public ChangeChannelCommand(TV tv){
        this.tv = tv;
    }

    @Override
    public void excute() {
        Log.e(TAG, "这个台不好看,我要换台");
        tv.changeChannel();
    }
}
/**
 * 具体的命令角色——减小音量
 */
public class DownVolumeCommand implements Command {

    private static final String TAG = "XXX";
    private TV tv;

    public DownVolumeCommand(TV tv){
        this.tv = tv;
    }

    @Override
    public void excute() {
        Log.e(TAG, "声音太大,我要减小音量");
        tv.downVolume();
    }
}
/**
 * 具体的命令角色——加大音量
 */
public class UpVolumeCommand implements Command {

    private static final String TAG = "XXX";
    private TV tv;

    public UpVolumeCommand(TV tv){
        this.tv = tv;
    }

    @Override
    public void excute() {
        Log.e(TAG, "声音太小,我要加大音量");
        tv.upVolume();
    }
}

从上面具体的命令中可以看到,都持有了电视机(命令接收者角色)的引用,在执行命令的方法中调用了电视机的具体方法,因为所有的命令都是由命令接收者来最终完成了,所以来让命令接收者执行真正的操作。来看看电视机(命令接收者角色)具体代码。

/**
 * 命令接收者角色——电视机
 */
public class TV {

    private static final String TAG = "XXX";

    public void downVolume() {
        Log.e(TAG, "电视机——减小音量");
    }

    public void upVolume() {
        Log.e(TAG, "电视机——加大音量");
    }

    public void changeChannel() {
        Log.e(TAG, "电视机——换台");
    }
}

最后是命令请求者角色,也就是遥控器,这里会发送相关的命令对象给电视机(命令接收者)。

/**
 * 命令请求者角色——遥控器
 */
public class TVControl {

    private Command command;

    public TVControl(Command command){
        this.command = command;
    }
    
    public void setCommand(Command command){
        this.command = command;
    }

    //发送请求
    public void doAction(){
        command.excute();
    }
}

我们来进行代码试。首先创建了电视机对象,再创建了具体的命令对象并将电视机对象传入,使得命令对象持有电视机对象的引用,最后是创建遥控器对象,并将命令对象传入,执行遥控器对象的doAction方法,发送命令,最后在电视机中执行操作。打印输出如下。

TV tv = new TV();
ChangeChannelCommand changeChannelCommand = new ChangeChannelCommand(tv);
DownVolumeCommand downVolumeCommand = new DownVolumeCommand(tv);
UpVolumeCommand upVolumeCommand = new UpVolumeCommand(tv);
TVControl control = new TVControl(changeChannelCommand);
control.doAction();
Log.e(TAG, "----------------------");
control.setCommand(downVolumeCommand);
control.doAction();
Log.e(TAG, "----------------------");
control.setCommand(upVolumeCommand);
control.doAction();

三、总结

命令模式使得命令的发送与接收解耦,这样使得代码容易维护和展;命令的添加很方便,并且可以方便的制定各种命令和利用现有命令组合出新的命令,如当需要创建比如开关机命令时,无需修改原逻辑代码,只需要创建一个新的命令对象;如果针对每一类具有共同接口的接收者制作一个调用者,可以控制命令的执行情况。

当然命令模式的缺点就是当命令较多时,也会同时增加类的数量,也就增加了系统的复杂性。

github地址:github.com/leewell5717…

四、参考

《JAVA与模式》之命令模式