• JavaScript设计模式与开发实践---读书笔记(9) 命令模式


    命令模式的用途:

    命令模式是最简单和优雅的模式之一,命令模式中的命令(command)指的是一个执行某些特定事情的指令。

    命令模式最常见的应用场景是:有时候需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是什么。此时希望用一种松耦合的方式来设计程序,使得请求发送者和请求接收者能够消除彼此之间的耦合关系。

    命令模式的例子-菜单程序:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>命令模式-菜单程序</title>
    </head>
    <body>
        <button id="button1">1</button>
        <button id="button2">2</button>
        <button id="button3">3</button>
    </body>
    </html>
    <script>
        var button1 = document.getElementById('button1');
        var button2 = document.getElementById('button2');
        var button3 = document.getElementById('button3');
        var setCommand = function(button,command){    
            button.onclick = function(){
                command.execute();
            }
        };
    
        var MenuBar = {
            refresh: function(){
                console.log('刷新菜单目录');
            },
            add: function(){
                console.log('增加菜单目录');
            },
            del: function(){
                console.log('删除子菜单');
            }
        };
    
        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(){
            console.log('删除子菜单');
        };
    
        var refreshMenuBarCommand = new RefreshMenuBarCommand(MenuBar);
        var addSubMenuCommand = new AddSubMenuCommand(SubMenu);
        var delSubMenuCommand = new DelSubMenuCommand(SubMenu);
    
        setCommand(button1,refreshMenuBarCommand);
        setCommand(button2,addSubMenuCommand);
        setCommand(button3,delSubMenuCommand);
    </script>

    以上代码模拟传统面向对象语言的命令模式实现。

    命令模式的由来,其实是回调函数的一个面向对象的替代品。

    var setCommand = function(button,func){    
            button.onclick = function(){
                func();
            }
        };
    
        var MenuBar = {
            refresh: function(){
                console.log('刷新菜单目录');
            }
        };
    //用闭包实现的命令模式如下代码所示:
        var RefreshMenuBarCommand = function(receiver){
            return function(){
                receiver.refresh();
            }
        };
        
        var refreshMenuBarCommand = new RefreshMenuBarCommand(MenuBar);
    
        setCommand(button1,refreshMenuBarCommand);
    //把执行函数改为调用execute方法
        var refreshMenuBarCommand = function(receiver){
            return{
                execute: function(){
                    receiver.refresh();
                }
            }
        };
    
        var setCommand = function(button,command){
            button.onclick = function(){
                command.execute();
            }
        };
    
        var refreshMenuBarCommand = new RefreshMenuBarCommand(MenuBar);
        setCommand(button1,refreshMenuBarCommand);

    撤销命令:

    撤销操作的实现一般是给命令对象增加一个名为unexecute或者undo的方法,在该方法里执行exectue的反向操作。

    撤消和重做:

    很多时候,我们需要撤销一系列的命令。然而,在某些情况下无法顺利地利用undo操作让对象回到execute之前的状态。

    canvas画图中,擦除一条线相对不容易实现。这时候最好的办法是先清除画布,然后把刚才执行过的命令全部重新执行一遍,这一点同样可以利用一个历史列表堆栈办到。记录命令日志,然后重复执行它们,这是逆转不可逆命令的一个好办法。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>撤销和重做命令</title>
    </head>
    <body>
        <button id="replay">播放录像</button>
    </body>
    </html>
    <script>
        var Ryu = {
            attack: function(){
                console.log('攻击');
            },
            defense: function(){
                console.log('防御');
            },
            jump: function(){
                console.log('跳跃');
            },
            crouch: function(){
                console.log('蹲下');
            }
        };
    
        var makeCommand = function(receiver,state){
            return function(){
                receiver[state]();
            }
        };
    
        var commands = {
            "119":"jump",//W
            "115":"crouch",//S
            "97":"defense",//A
            "100":"attack"//D
        };
    
        var commandStack = [];    //保存命令的堆栈
        document.onkeypress = function(ev){
            var keyCode = ev.keyCode,
                command = makeCommand(Ryu,commands[keyCode]);
            if(command){
                command();    //执行命令
                commandStack.push(command);//将刚刚执行过的命令保存进堆栈
    
            }
        };
    
        document.getElementById('replay').onclick = function(){    //点击播放录像
            var command;
            while(command = commandStack.shift()){    //从堆栈里依次取出命令并执行
                command();
            }
        };
    </script>

    命令队列

    宏命令:

    宏命令是一组命令的集合,通过执行宏命令的方式,可以一次执行一批命令。

        var closeDoorCommand = {
            execute: function(){
                console.log('关门');
            }
        };
    
        var openPcCommand = {
            execute: function(){
                console.log('开电脑');
            }
        };
    
        var openQQCommand = {
            execute: function(){
                console.log('登录QQ');
            }
        };
    
        var MacroCommand = function(){
            return {
                commandsList:[],
                add: function(){
                    for(var i=0,command;command = this.commandsList[i++]; ){
                        command.execute();
                    }
                }
            }
        };
    
        var macroCommand = MacroCommand();
        macroCommand.add(closeDoorCommand);
        macroCommand.add(openPcCommand);
        macroCommand.add(openQQCommand);
        macroCommand.execute();

    宏命令是命令模式与组合模式的联用产物。

    智能命令与傻瓜命令

    命令模式在JavaScript语言中的一种隐形的模式。

  • 相关阅读:
    【java多线程】队列系统之说说队列Queue
    【传输协议】什么是CA证书
    5.1 javassist基本使用
    第四章 dubbo内核之aop源码解析
    第三章 dubbo内核之ioc源码解析
    2.2 dubbo-spi源码解析
    2.1 jdk-spi的实现原理
    第一章 第一个dubbo项目
    第零章 dubbo源码解析目录
    macOS Sierra10.12.5 显示允许任何来源
  • 原文地址:https://www.cnblogs.com/6489c/p/5943430.html
Copyright © 2020-2023  润新知