美文网首页
命令模式二

命令模式二

作者: 三流之路 | 来源:发表于2022-10-03 10:03 被阅读0次

与中介者的关系

就比如中介者模式那里,BangZi 在交保护费的时候,还看 GuiZi 交没交,交多少,底层横向发生联系。

public BangZiPayCommand extends Command {
    public BangZiPayCommand(int money) {
        super(money);
    }
    public void execute() {
        // 比如 GuiZi 交多少有个方法返回
        if (gz.getPayMoney() < 4) {
            gz.pay(money / 2)
        } else {
            gz.pay(money);
        }
        gz.bite(false);
        gz.build(false);
    }
}

那这我怎么又感觉回到中介者了呢,这两个模式分界有点越来越模糊的感觉。感觉命令就是中介者的再一次封装,原来的中介类会变得臃肿,命令将中介者内部的 if/else 抽象出一种 Command,将具体逻辑挪到 Command 里。等于说将 Mediator 一个大的类拆分成许多个 Command,就是单一职责,原来的中介者职责太多,现在一个职责抽取一个 Command。

上一节中介者模式例子是这么写的

public abstract class AbstractMediator { 
    protected DengTa dt; 
    protected GuiZi gz;  
    protected BangZi bz;

    // setter/getter

    //中介者最重要的方法叫做事件方法,处理多个对象之间的关系 
    // str 是具体指令,objects 是可能调用方法需要的参数 
    public abstract void execute(String str,Object...objects);
}

public class Mediator extends AbstractMediator {
    public void execute(String str,Object...objects){ 
        if(str.equals("dengTa.shouBHF")){ //灯塔收保护费
            this.dengTaShouBHF(); 
        }else if(str.equals("guiZi.jiaoBHF")){ //鬼子交保护费
            this.guiZiJiaoBHF(); 
        }else if(str.equals("bangZi.jiaoBHF")){ //棒子交保护费
            this.bangZiJiaoBHF();  
        }
    }

    private void dengTaShouBHF() {
        gz.jiaoBHF(4); // 鬼子上交 4 千亿
        bz.jiaoBHF(1); // 棒子上交 1 千亿
    }


    private int guiZiJiaoBHF() {
        int bhf = number;
        if (bz.maGuiZi()) { // 棒子骂我
            if (dt.shouShi("BangZi")) { // 爸爸帮我收拾它一顿
                bhf = number * 2; // 心情好,交两倍
            } else { // 爸爸没有帮我收拾
                bhf = number / 2; // 心情不好,少交一半
            }
        }
        bhf = number;
        gz.setBHF(bhf);
        System.out.println("给爸爸交保护费:" + bhf);
    }

    private int bangZiJiaoBHF() {
        int gzBHF = gz.getBFH();
        if (gzBHF == 0) {
            System.out.println("他还没交,主人先从他那收到钱,我再给");
        } else if (gzBHF < 4) {
            System.out.println("他才交了这么一点,那我只能交保护费 " + number/2);
        } else {
            System.out.println("给主人交保护费:" + number);
        }
    }

}

改造一下试试,将 Mediator 抽取出来的单一职责的逻辑变成 Command,然后自己变成 Invoker。

// 原来的 AbstractMediator
public abstract class Command { 
    // 接收者 Receiver
    protected DengTa dt; 
    protected GuiZi gz;  
    protected BangZi bz;

    // setter/getter

    // 不需要指令参数 str 了,Command 类型本身就代表了什么指令
    // 我上面例子参数是通过 Command 的构造方法传进去的,这里通过
    // 执行方法参数,区别不大
    public abstract void execute(Object...objects);
}

// dengTa.shouBHF 变成一个具体 Command
public class DengTaShouBHFCommand extend Command {
    public void execute(Object...objects) {
        this.dengTaShouBHF(); 
    }
    private void dengTaShouBHF() {
        gz.jiaoBHF(4); // 鬼子上交 4 千亿
        bz.jiaoBHF(1); // 棒子上交 1 千亿
    }
}

// guiZi.jiaoBHF 变成一个具体 Command
public class GuiZiJiaoBHFCommand extend Command {
    public void execute(Object...objects) {
        this.guiZiJiaoBHF();
    }
    private int guiZiJiaoBHF() {
        int bhf = number;
        if (bz.maGuiZi()) { // 棒子骂我
            if (dt.shouShi("BangZi")) { // 爸爸帮我收拾它一顿
                bhf = number * 2; // 心情好,交两倍
            } else { // 爸爸没有帮我收拾
                bhf = number / 2; // 心情不好,少交一半
            }
        }
        bhf = number;
        gz.setBHF(bhf);
        System.out.println("给爸爸交保护费:" + bhf);
    }
}

// bangZi.jiaoBHF 同样是一个 Command

// 就是原来的 Mediator
public class InVoker {
    private Command cmd; 
    // 通过 setter 方法
    public void setCommand(Command cmd) {
        this.cmd = cmd;
    }

    // 这方法就是原来的 execute,为了和 Command 的 execute 区分
    // 写成 action,其实写成 execute 也行,
    // str 参数不需要了,因为 setCommand 取代了这个功能
    public void action(Object...objects){ 
        // 原来 if/else 判断不需要了,因为逻辑分散到具体 Command 了
        cmd.execute();
    }
}

我去,真的感觉两者本质是一样的了。中介者是一层封装,命令是在中介者基础上再一次封装。且没什么高层低层的区别,都是一样的,即便低层跪着给高层请安,那也可以是一个 Command。

撤销

命令本身有撤销的逻辑。

public abstract class Command {  
    // 其它语句
    public abstract void execute();
    // 撤销方法
    public abstract void undo();
}

public GuiZiPayCommand extends Command {
    public GuiZiPayCommand(int money) {
        super(money);
    }
    public void execute() {
        gz.pay(money);
        gz.bite(false);
        gz.build(false);
    }
    public void undo() {
        // 内部做撤销逻辑,比如原来是 build(true),bite(true) 的
        gz.build(true);
        gz.bite(true);
        gz.back(money); // 比如还有个把钱收回来的方法
        
    }
}

然后中间的发布者要内部记录下执行的命令,这样方便找到并撤销。

public class Invoker {
    private List<Command> cmdList; // 记录命令列表
    public void setCommand(Command command) {
        cmdList.add(command);
    }
    public void action() {
        cmdList.get(command.size()-1).execute();
    }
    public void unAction() { // 删掉最后一个命令,并撤销
        cmdList.remove(command.size()-1).undo();
    }
}
public class FatherDengTa() {
    private Invoker invoker = new Invoker(); // 中介
    
    public void shouGuiZiBHF() {
        Command cmd = new GuiZiPayCommand(1);
        invoker.setCommand(cmd);
        invoker.action();
        // GuiZi 欢欣鼓舞,一分钟过去了
        // 说弄错了,少说一个 0,先撤销
        invoker.unAction(); 
    }
}

通过状态值撤销,适合命令本身也就是设置一些不同的状态,然后执行命令时记录下当前状态,撤销就是根据记下来的状态再回滚回去。

宏编程

就是一个命令封装若干个其它命令,然后调用它一次性执行多个。

public SonPayCommand extends Command {
    private Command[] cmds;
    // 由其它命令数组构造
    public SonPayCommand(Command[] cmds) {
        this.cmds = cmds;
    }
    public void execute() {
        for (int i=0; i<cmds.length; i++) {
            cmds[i].execute();
        }
    }
}

public class FatherDengTa() {
    private Invoker invoker = new Invoker(); // 中介

    // 爸爸不需要发一个命令给儿子,再发一个命令给另一个儿子
    // 爸爸把命令直接打包好,一起发出去
    public void shouSonBHF() {
        Command cmd = new GuiZiPayCommand(4);
        Command cmd2 = new BangZiPayCommand(1);
        Command[] cmds = { cmd, cmd2 };
        SonPayCommand sonCmd = new SonPayCommand(cmds);
        invoker.setCommand(sonCmd);
        invoker.action();
    }
}

其它应用

  1. 队列请求:由于 Command 内部已经封装了接收者和具体执行的动作,所以可以用命令封装后放入一个队列,其它线程读取队列,取出 Command,调用它的 execute,接收者比如 GuiZi 就自己把事情办了。
  2. 日志请求:还是由于 Command 封装了接收者和动作的特性,一些操作过程中,每次都记录一个 Command 当做日志,如果系统死机了,就可以从日志中取出命令,再一次一次的执行,就能够恢复到死机前的状态。如果倒过来,一个一个撤销,就可以恢复到之前的某种状态。

相关文章

  • 命令模式

    一、命令模式介绍 二、命令模式代码实例

  • 第5章 -行为型模式-命令模式

    一、命令模式的简介 二、命令模式的优缺点 三、命令模式的实例

  • 基础-18、vim(二)

    笔记内容:5.5 进入编辑模式5.6 vim命令模式5.7 vim实践 一、进入编辑模式 二、vim命令模式 Q:...

  • 命令模式二

    接收者 TerisMachine 命令接口 TMCommandProtocol 命令 请求者 TerisMachi...

  • 命令模式二

    与中介者的关系 就比如中介者模式那里,BangZi 在交保护费的时候,还看 GuiZi 交没交,交多少,底层横向发...

  • 设计模式(8)-命令模式详解(易懂)

    命令模式的定义 命令模式,将一组行为抽象为对象,实现二者之间的松耦合。这就是命令模式(Command Patter...

  • vim 编辑器常用命令及三种模式说明

    本文只列出我目前常用的命令 三种模式: 一、光标移动(命令行模式下) 二、复制、粘贴、删除、撤销、编辑(命令行模式...

  • vim编辑模式、vim命令模式

    目录 一、 进入编辑模式二、vim命令模式三、vim实践 一、 进入编辑模式 当我们使用命令Vim filenam...

  • 三、Jupyter快捷键

    一、编辑模式 绿色框,编辑内部内容 切换 运行(命令模式也可用) 提示和操作 二、命令模式 切换 整体操作 单元操...

  • 设计模式-命令模式(二)

    前言 由于设计模式-命令模式(一)中讲到,如果类太多导致很多冗余,并且增加工作量及不利于维护等,在本次讲中我们需要...

网友评论

      本文标题:命令模式二

      本文链接:https://www.haomeiwen.com/subject/vexeartx.html