前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >命令模式二

命令模式二

作者头像
三流编程
发布2022-10-04 08:36:23
1450
发布2022-10-04 08:36:23
举报

与中介者的关系

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

代码语言:javascript
复制
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。

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

代码语言:javascript
复制
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。

代码语言:javascript
复制
// 原来的 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。

撤销

命令本身有撤销的逻辑。

代码语言:javascript
复制
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); // 比如还有个把钱收回来的方法
        
    }
}

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

代码语言:javascript
复制
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();
    }
}
代码语言:javascript
复制
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(); 
    }
}

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

宏编程

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

代码语言:javascript
复制
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 当做日志,如果系统死机了,就可以从日志中取出命令,再一次一次的执行,就能够恢复到死机前的状态。如果倒过来,一个一个撤销,就可以恢复到之前的某种状态。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-10-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 与中介者的关系
  • 撤销
  • 宏编程
  • 其它应用
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档