狠狠的学 -- 命令模式

        命令模式主要用于解耦请求者与执行者之间的关系。有些场景,请求者并不需要知道操作的具体执行者、操作具体怎么做,但是仍需要发送请求。命令模式就很适合这种场景(比如,订餐)。

        传统面向对象设计语言实现命令模式的两个关键点命令对象command以及命令类***Command。具体的执行者被封装成命令类的receiver属性,在execute方法内执行receiver.xxx具体的操作(将执行操作被封装成commandexecute方法)。请求者会持有command,发起请求就调用command.execute方法。命名是相互约定的,不是固定的。

        这样就形成了一种松耦合的关系,请求者与执行者之间通过command维持联系,其他信息相互隔离开来,可以将二者(请求者、执行者)分工同时进行。但是这种实现方式会创建很多命令类,看下例。

        JS 模拟传统面向对象语言实现命令模式:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <button id="button1">点击按钮1</button>
    <button id="button2">点击按钮2</button>
    <button id="button3">点击按钮3</button>
    <script>
        var button1 = document.getElementById('button1');
        var button2 = document.getElementById('button2');
        var button3 = document.getElementById('button3');
        // 菜单例子 - 模拟传统面向对象语言 - 命令模式
        // 请求者只需要执行 setCommand 函数, 持有 command 对象
        function setCommand(button, command) {
            button.onclick = function() {
                command.execute();
            }
        }
        // 具体的执行者,以及具体的操作
        var MenuBar = {
            refresh() {
                console.log('刷新菜单目录');
            }
        }
        var SubMenu = {
            add() {
                console.log('增加子菜单');
            },
            del() {
                console.log('删除子菜单');
            }
        }
        // 为每个执行者的每个具体操作都创建对应的命令类,并创建约定好的 execute 方法
        // 本例中有 2 个执行者,3 个具体操作,相当于要创建 3 个命令类
        var RefreshMenuBarCommand = function (receiver) {
            this.receiver = receiver;
        }
        RefreshMenuBarCommand.prototype.execute = function() {
            this.receiver.refresh();
        }
        var AddSubMenuCommand = function (receiver) {
            this.receiver = receiver;
        }
        AddSubMenuCommand.prototype.execute = function() {
            this.receiver.add();
        }
        var DelSubMenuCommand = function (receiver) {
            this.receiver = receiver;
        }
        DelSubMenuCommand.prototype.execute = function() {
            this.receiver.del();
        }
        // 通过 setCommand 给每个按钮添加 command 对象
        setCommand(button1, new RefreshMenuBarCommand(MenuBar));
        setCommand(button2, new AddSubMenuCommand(SubMenu));
        setCommand(button3, new DelSubMenuCommand(SubMenu));
    </script>
</body>
</html>

  JS不同,不需要这么复杂。通过 高阶函数 + 闭包 就可以实现。闭包函数可以像command对象一样,通过调用闭包函数就可以替代调用command.execute这一操作。优缺点都是一样的(松耦合、很多命令类)。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <button id="button1">点击按钮1</button>
    <button id="button2">点击按钮2</button>
    <button id="button3">点击按钮3</button>
    <script>
        var button1 = document.getElementById('button1');
        var button2 = document.getElementById('button2');
        var button3 = document.getElementById('button3');
        // 菜单例子 - 命令模式 - JS 自身特点
        // 用 函数 代替了 command 对象
        function setCommand (button, execute) {
            button.onclick = function() {
                execute();
            }
        }
        // 具体的执行者,以及具体的操作
        var MenuBar = {
            refresh() {
                console.log('刷新菜单目录');
            }
        }
        var SubMenu = {
            add() {
                console.log('增加子菜单');
            },
            del() {
                console.log('删除子菜单');
            }
        }
        // 命令类的实现用 高阶函数 + 闭包 完成
        var RefreshMenuBarCommand = function (receiver) {
            return function() {
                receiver.refresh();
            }
        }
        var AddSubMenuCommand = function(receiver) {
            return function() {
                receiver.add();
            }
        }
        var DelSubMenuCommand = function(receiver) {
            return function() {
                receiver.del();
            }
        }
        var refreshMenuBarCommand = RefreshMenuBarCommand(MenuBar);
        var addSubMenuCommand = AddSubMenuCommand(SubMenu);
        var delSubMenuCommand = DelSubMenuCommand(SubMenu);
        setCommand(button1, refreshMenuBarCommand);
        setCommand(button2, addSubMenuCommand);
        setCommand(button3, delSubMenuCommand);
    </script>
</body>
</html>

        还可以实现撤销重做功能。

        撤销操作一般约定命名为unexecute or undo,主要实现思路是在执行execute之前保存下请求者的状态,在执行unexecute方法时,利用保存的状态还原。

        撤销一般只能撤销一步。撤销一系列命令或者操作的思路是重做。主要实现思路是将请求者发起的命令保存在一个历史记录队列中,在redo的时候先重置当前状态,然后执行历史记录队列中的命令。

        动画场景用的较多的队列功能。

        如果用户的点击过快,可能会导致第一个动画突然停止,立马切到第二个动画。这种体验肯定不好,最好的体验肯定是流畅且连贯的画面。命令对象的生命周期是较早且长的,可以将HTML元素的动画过程封装成命令对象,然后压进队列,当一个任务执行完,再通知(通知机制可以用回调函数发布-订阅)队列,弹出并执行下一个任务。

        宏命令就是一些列命令的组合,调用一个宏命令就就可以执行一批命令。与之前的实现不同,命令类没有receiver属性,execute方法没有定义在原型上,而是定义为自身属性。这种实现方式叫智能命令,而之前的实现都是傻瓜式命令

// 宏命令 - JS 自身特点实现
// 场景:万能遥控器的一个特别按钮, 可以干 3 件事,关门、打开电脑、登录QQ
// 首先创建好各种 command 对象
var closeDoorCommand = {
    execute() {
        console.log(('关门'));
    }
}
var openPcCommand = {
    execute() {
        console.log('开电脑');
    }
}
var openQQCommand = {
    execute() {
        console.log('登录QQ');
    }
}
// 然后定义 宏命令
var MacroCommand = function() {
    return {
        commandList: [],
        add(command) {
            this.commandList.push(command);
        },
        execute() {
            this.commandList.forEach(command => command.execute());
        }
    }
}
// 创建宏命令对象并添加子命令
var macroCommand = MacroCommand();
macroCommand.add(closeDoorCommand);
macroCommand.add(openPcCommand);
macroCommand.add(openQQCommand);
macroCommand.execute();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值