命令模式(Command)

115 阅读3分钟

定义

将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或者记录请求日志,以及支持可撤销操作。

本质

封装请求

登场角色

  • Command(命令)

    负责定义命令的接口

  • ConcreteCommand(具体的命令)

    实现命令接口

  • Receiver(接收者)

    命令接收者,真正执行命令的对象,任何对象都可以成为命令接收者,只要它能够实现命令要求实现的相应功能。

  • Client(请求者)

    创建具体的命令对象并设置命令接收者,即组装命令对象和接收者。

  • Invoker(发动者)

    开始执行命令的角色。通常持有命令对象,可以持有很多命令对象,相当于命令对象的入口。

示例代码

/**
 * 接收者角色,处理具体逻辑
 * 是整个命令模式中唯一处理具体代码逻辑的地方,
 * 其他的类都是直接或间接的调用到该类的方法
 */
public class TerisMachine {
    /**
     * 真正处理向左操作的逻辑
     */
    public void toLeft(){
        System.out.println("向左");
    }

    /**
     * 真正处理向右操作的逻辑
     */
    public void toRight(){
        System.out.println("向右");
    }

    /**
     * 真正处理快速落下操作的逻辑
     */
    public void fastToBottom(){
        System.out.println("快速落下");
    }

    /**
     * 真正处理改变形状操作的逻辑
     */
    public void transform(){
        System.out.println("改变形状");
    }
}

/**
 * 命令抽象者
 */
public interface Command {
    void execute();
}


/**
 * 具体命令,持有一个接收者角色对象
 */
public class LeftCommand implements Command{
    private TerisMachine terisMachine;

    public LeftCommand(TerisMachine terisMachine) {
        this.terisMachine = terisMachine;
    }

    @Override
    public void execute() {
        terisMachine.toLeft();
    }
}


/**
 * 具体的命令
 */
public class RightCommand implements Command{
    private TerisMachine terisMachine;

    public RightCommand(TerisMachine terisMachine) {
        this.terisMachine = terisMachine;
    }

    @Override
    public void execute() {
        terisMachine.toRight();
    }
}

/**
 * 具体的命令
 */
public class FallCommand implements Command{
    private TerisMachine terisMachine;

    public FallCommand(TerisMachine terisMachine) {
        this.terisMachine = terisMachine;
    }

    @Override
    public void execute() {
        terisMachine.fastToBottom();
    }
}

/**
 * 具体的命令
 */
public class TransforCommand implements Command{
    private TerisMachine terisMachine;

    public TransforCommand(TerisMachine terisMachine) {
        this.terisMachine = terisMachine;
    }

    @Override
    public void execute() {
        terisMachine.transform();
    }
}


/**
 * 请求者
 */
public class Buttons {
    private LeftCommand leftCommand;
    private RightCommand rightCommand;
    private FallCommand fallCommand;
    private TransforCommand transforCommand;

    public void setLeftCommand(LeftCommand leftCommand) {
        this.leftCommand = leftCommand;
    }

    public void setRightCommand(RightCommand rightCommand) {
        this.rightCommand = rightCommand;
    }

    public void setFallCommand(FallCommand fallCommand) {
        this.fallCommand = fallCommand;
    }

    public void setTransforCommand(TransforCommand transforCommand) {
        this.transforCommand = transforCommand;
    }

    public void toLeft(){
        leftCommand.execute();
    }

    public void toRight(){
        rightCommand.execute();
    }

    public void fall(){
        fallCommand.execute();
    }

    public void transform(){
        transforCommand.execute();
    }
}


public class Client {
    public static void main(String[] args){
        //构造俄罗斯方块游戏
        TerisMachine terisMachine = new TerisMachine();
        //构造四种命令
        LeftCommand leftCommand = new LeftCommand(terisMachine);
        RightCommand rightCommand = new RightCommand(terisMachine);
        FallCommand fallCommand = new FallCommand(terisMachine);
        TransforCommand transforCommand = new TransforCommand(terisMachine);
        //按钮可以执行不同的命令
        Buttons buttons = new Buttons();
        buttons.setLeftCommand(leftCommand);
        buttons.setRightCommand(rightCommand);
        buttons.setFallCommand(fallCommand);
        buttons.setTransforCommand(transforCommand);
        //具体按下哪个按钮玩家说了算
        buttons.toLeft();
        buttons.toRight();
        buttons.fall();
        buttons.transform();
    }
}

运行结果

向左
向右
快速落下
改变形状

优点

  • 更松散的耦合

    命令模式使得发起命令的对象(客户端),和具体的命令的对象(接收者)对象完全解耦,也就是说发起命令的对象完全不知道具体实现对象是谁,也不知道如何实现。

  • 更动态的控制

    命令模式把请求封装起来,可以动态的对它进行参数化、队列化和日志化等操作,从而使得系统更加灵活。

  • 很自然的复合命令

    命令模式中的命令对象能够很容易的组合成复合命令,也就是前面讲的宏命令,从而使系统操作更简单,功能更强大。

  • 更好的扩展性

    由于发起命令的对象和具体的实现完全解耦,因此扩展新的命令就很容易,只需要实现新的命令对象,然后在装配的时候,把具体的实现对象设置到命令对象中,然后就可以使用这个命令对象,已有的实现完全不用变化。

何时使用

  • 需要抽象出待执行的动作,然后以参数的形式提供出来。类似于过程设计中的回调机制,而命令模式正式回调机制的一个面向对象的替代品。
  • 在不同的时候指定、排列和执行请求。一个命令对象可以有与初始请求无关的生命期。
  • 需要支持取消操作。
  • 支持修改日志功能,这样当系统崩溃时,这些修改可以被重做一次。
  • 需要支持事务操作。