美文网首页
设计模式之命令模式

设计模式之命令模式

作者: zhao_ran | 来源:发表于2021-08-22 18:47 被阅读0次

    命令模式中的命令(command)指的是一个执行某些特定事情的指令。

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

    拿订餐来说,客人需要向厨师发送请求,但是完全不知道这些厨师的名字和联系方式,也不知道厨师炒菜的方式和步骤。 命令模式把客人订餐的请求封装成 command 对象,也就是订餐中的订单对象。这个对象可以在程序中被四处传递,就像订单可以从服务员手中传到厨师的手中。这样一来,客人不需要知道厨师的名字,从而解开了请求调用者和请求接收者之间的耦合关系。

    命令模式的例子——菜单程序

    在大型项目开发中,这是很正常的分工。对于绘制按钮的程序员来说,他完全不知道某个按钮未来将用来做什么,可能用来刷新菜单界面,也可能用来增加一些子菜单,他只知道点击这个按钮会发生某些事情。那么当完成这个按钮的绘制之后,应该如何给它绑定 onclick 事件呢?

    回想一下命令模式的应用场景:

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

    我们很快可以找到在这里运用命令模式的理由:点击了按钮之后,必须向某些负责具体行为的对象发送请求,这些对象就是请求的接收者。但是目前并不知道接收者是什么对象,也不知道接收者究竟会做什么。此时我们需要借助命令对象的帮助,以便解开按钮和负责具体行为对象之间的耦合。

    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( '刷新菜单目录' );
        }
    };
    var SubMenu = {
        add: function(){
            console.log( '增加子菜单' );
        },
        del: function(){
            console.log( '删除子菜单' );
        }
    };
    
    var RefreshMenuCommand = function(receiver){
        this.receiver = receiver;
    }
    
    RefreshMenuCommand.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();
    }
    
    var refreshMenuCommand = new RefreshMenuCommand( MenuBar );
    var addSubMenuCommand = new AddSubMenuCommand( SubMenu );
    var delSubMenuCommand = new DelSubMenuCommand( SubMenu );
    
    setCommand(button1, refreshMenuCommand);
    setCommand(button2, addSubMenuCommand);
    setCommand(button3, delSubMenuCommand);
    
    JavaScript 中的命令模式

    也许我们会感到很奇怪,所谓的命令模式,看起来就是给对象的某个方法取了 execute 的名字。引入 command 对象和receiver 这两个无中生有的角色无非是把简单的事情复杂化了,即使不用什么模式,用下面寥寥几行代码就可以实现相同的功能:

    var bindClick = function( button, func ){
        button.onclick = func;
    };
    var MenuBar = {
        refresh: function(){
            console.log( '刷新菜单界面' );
        }
    };
    var SubMenu = {
        add: function(){
            console.log( '增加子菜单' );
        },
        del: function(){
            console.log( '删除子菜单' );
        }
    };
    bindClick( button1, MenuBar.refresh );
    

    命令模式将过程式的请求调用封装在 command对象的execute方法里,通过封装方法调用,我们可以把运算块包装成形。command对象可以被四处传递,所以在调用命令的时候,客户(Client)不需要关心事情是如何进行的。

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

    JavaScript作为将函数作为一等对象的语言,跟策略模式一样,命令模式也早已融入到了JavaScript语言之中。运算块不一定要封装在 command.execute方法中,也可以封装在普通函数中。函数作为一等对象,本身就可以被四处传递。即使我们依然需要请求“接收者”,那也未必使用面向对象的方式,闭包可以完成同样的功能。

    在面向对象设计中,命令模式的接收者被当成command对象的属性保存起来,同时约定执行命令的操作调用command.execute方法。在使用闭包的命令模式实现中,接收者被封闭在闭包产生的环境中,执行命令的操作可以更加简单,仅仅执行回调函数即可。无论接收者被保存为对象的属性,还是被封闭在闭包产生的环境中,在将来执行命令的时候,接收者都能被顺利访问。用闭包实现的命令模式如下代码所示:

    如果想更明确地表达当前正在使用命令模式,或者除了执行命令之外,将来有可能还要提供撤销命令等操作。那我们最好还是把执行函数改为调用 execute方法:

    var button1 = document.getElementById( 'button1' );
    
    var setCommand = function(button, command){
        button.onclick = function(){
            command.execute();
        }
    }
    
    var MenuBar = {
        refresh: function(){
            console.log( '刷新菜单目录' );
        }
    };
    
    var RefreshMenuCommand = function(receiver){
        return {
            execute: function(){
                receiver.refresh();
            }
        }
    }
    
    var refreshMenuCommand = new RefreshMenuCommand( MenuBar );
    
    setCommand(button1, refreshMenuCommand);
    

    相关文章

      网友评论

          本文标题:设计模式之命令模式

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