JAVA23种设计模式之命令模式

1. 命令模式:
把一个请求或者操作封装到一个对象中。从而允许系统使用不同的请求对客户进行参数化。对请求排队或者记录请求日志,还可以提供命令的撤销和恢复。是一种行为类模式。(说实话,这个定义基本无法让人理解!)。
命令模式是把每个命令进行封装,将命令请求发送者和命令接受者进行解耦。在这个模式中,请求者(持有具体的命令类)发送请求,具体的命令类(持有接收者)接受到后,执行接受者(具体操作的执行者)的具体操作方法,从而实现命令的执行。在这种模式下,命令请求者和命令命令接收者被完全分隔开来。请求者几乎不需要知道接收者的任何信息。
2. 命令模式包含的角色:

  • 接收者角色: 负责执行或者实施具体的命令。所有类都可以成为接收者,去执行对应的行动方法。
  • 抽象命令角色: 是一个给所有命令类继承的借口或者实现的抽象类,一般包含一个execute()方法。通过这个角色中的相关方法可以处理请求者的相关请求。
  • 具体命令角色: 实现或继承类抽象命令接口或者类,它持有具体具体的接受者,并且将接收者和对应的命令或者动作进行绑定。并且将execute()方法绑定接收者的具体操作。
  • 请求者角色: 持有对象命令,负责调用对象实现请求,一般情况下,该类通过与抽象命令角色之间进行关联。
    3. 命令模式简单类图:
    在这里插入图片描述
    4. 命令模式示例代码:
    场景:以常见的电视机遥控器为例。假设该遥控器有6个按钮,分别为:电视机开/关,音量加/减,频道加/减。
    接收者角色:这里定义为电视机。
public class Television {
    public void onTV(){
        System.out.println("电视机正在启动中......");
    }
    public void offTV(){
        System.out.println("电视机正在关闭中......");
    }
    public void volDown(){
        System.out.println("电视机音量减小一度......");
    }
    public void volUp(){
        System.out.println("电视机音量增加一度......");
    }
    public void channelUp(){
        System.out.println("电视频道增+1......");
    }
    public void channelDown(){
        System.out.println("电视机频道增加-1......");
    }
}

抽象命令角色:

public interface Command {
    void execute();
}

具体命令角色:
电视机开/关:

public class OffTvCommand implements Command {
    //持有接收者对象
    private Television television;
    public OffTvCommand(Television television) {
        this.television = television;
    }
    @Override
    public void execute() {
        television.offTV();
    }
}
public class OnTvCommand  implements Command{
    private Television television;
    public OnTvCommand(Television television) {
        this.television = television;
    }
    @Override
    public void execute() {
        television.onTV();
    }
}

音量加/减:

public class VolDownCommand implements Command {
    private Television television;
    public VolDownCommand(Television television) {
        this.television = television;
    }
    @Override
    public void execute() {
        television.volDown();
    }
}
public class VolUpCommand implements Command {
    private Television television;
    public VolUpCommand(Television television) {
        this.television = television;
    }
    @Override
    public void execute() {
        television.volUp();
    }
}

频道加/减:

public class ChannelDownCommand implements Command {
    private Television television;
    public ChannelDownCommand(Television television) {
        this.television = television;
    }
    @Override
    public void execute() {
        television.channelDown();
    }
}
public class ChannelUpCommand implements Command {
    private Television television;
    public ChannelUpCommand(Television television) {
        this.television = television;
    }
    @Override
    public void execute() {
        television.channelUp();
    }
}

请求者角色:这里定义为遥控器以及测试主函数:
遥控器:

public class ControlPanel {
    private static final int CONTROL_SIZE = 7;
    private Command[] commands;
    /**
     * 初始化所有按钮指向空的按钮,避免后面出现异常
     */
    public ControlPanel() {
        commands = new Command[CONTROL_SIZE];
        for (int i = 0; i <commands.length ; i++) {
            commands[i]=new NoCommand();
        }
    }
    /**
     * 把命令绑定到对应的键上
     * @param index
     * @param command
     */
    public void addCommand(int index ,Command command){
        commands[index] =  command;
    }
    /**
     * 模拟点击按钮
     * @param index
     */
    public void press(int index){
        commands[index].execute();
    }
}

测试主函数:

public class Main {
    public static void main(String[] args) {
        //初始化遥控器
        ControlPanel controlPanel = new ControlPanel();
        //初始化接收者
        Television television = new Television();
        //为遥控器上的键设置命令
        controlPanel.addCommand(0,new ChannelDownCommand(television));
        controlPanel.addCommand(1,new ChannelUpCommand(television));
        controlPanel.addCommand(2,new OffTvCommand(television));
        controlPanel.addCommand(3,new OnTvCommand(television));
        controlPanel.addCommand(4,new ChannelDownCommand(television));
        controlPanel.addCommand(5,new ChannelUpCommand(television));
        //点击按钮
        controlPanel.press(5);
        controlPanel.press(4);
        controlPanel.press(3);
        controlPanel.press(2);
        controlPanel.press(1);
        controlPanel.press(0);
    }
}

另外附上Nocommand源码:

public class NoCommand implements Command {
    public NoCommand() {
    }
    @Override
    public void execute() {
        System.out.println("该按钮暂时还未绑定命令!");
    }
}

进阶版: 但是用户又要求按下某个按钮后,既可以实现打开电视机,又能加大一度音量,频道在原来基础上减一。
这种情况下,我们就需要新增一个命令了。具体代码实现如下:

public class GroupCommand  implements Command{
    private List<Command> commandList = new ArrayList<Command>();
    /**
     * 添加系列命令
     * @param command
     */
    public void addCommand(Command command){
        commandList.add(command);
    }
    /**
     * 移除命令
     * @param command
     */
    public  void removeCommand(Command command){
        commandList.remove(command);
    }
    @Override
    public void execute() {

        for (Command command:commandList
             ) {
            command.execute();
        }
    }
}

测试主函数;

public class Main {
    public static void main(String[] args) {
        //初始化遥控器
        ControlPanel controlPanel = new ControlPanel();
        //初始化接收者
        Television television = new Television();
        //初始化系类命令
        GroupCommand groupCommand = new GroupCommand();
        groupCommand.addCommand(new OnTvCommand(television));
        groupCommand.addCommand(new ChannelUpCommand(television));
        groupCommand.addCommand(new VolDownCommand(television));
        //为遥控器上的键设置命令
        controlPanel.addCommand(0,new ChannelDownCommand(television));
        controlPanel.addCommand(1,new ChannelUpCommand(television));
        controlPanel.addCommand(2,new OffTvCommand(television));
        controlPanel.addCommand(3,new OnTvCommand(television));
        controlPanel.addCommand(4,new VolDownCommand(television));
        controlPanel.addCommand(5,new VolUpCommand(television));
        controlPanel.addCommand(6,groupCommand);
        //点击按钮
        controlPanel.press(5);
        controlPanel.press(4);
        controlPanel.press(3);
        controlPanel.press(2);
        controlPanel.press(1);
        controlPanel.press(0);
        //执行系列命令
        System.out.println("==================分割线==================");
        controlPanel.press(6);
    }
}

以上代码可以看出命令模式具有以下优点:

  1. 降低系统的耦合度:命令的发起者和接受者完全解耦合,请求者和接受者之间可以进行自由组合,不会互相影响,具有良好的独立性。
  2. 更加良好的扩展性:因为新增加的具体命令类不会影响到其他类。满足JAVA中“开闭原则”的要求。
  3. 更简便的组合命令:可以比较容易地设计一个命令队列或宏命令(组合命令)。
  4. 命令可以支持撤销,实现一个undo()方法来回到execute()被执行前的状态。
    以上代码也可以看出命令模式存在一个很明显的缺点:
    当命令很多的时候,会出现很多很多的具体命令类。
    总结:
    命令模式的关键在于引入了抽象命令类,请求发送者针对抽象命令类编程,只有实现了抽象命令类的具体命令才与请求接收者相关联。在最简单的抽象命令类中只包含了一个抽象的execute()方法,每个具体命令类将一个Receiver类型的对象作为一个实例变量进行存储,从而具体指定一个请求的接收者,不同的具体命令类提供了execute()方法的不同实现,并调用不同接收者的请求处理方法。
发布了62 篇原创文章 · 获赞 8 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/system_obj/article/details/87958347