美文网首页Frond-End程序员开源工具技巧
JS设计模式3 - The Mediator Pattern,E

JS设计模式3 - The Mediator Pattern,E

作者: 转角遇见一直熊 | 来源:发表于2017-03-17 15:32 被阅读42次

    中介者模式

    中介者模式

    目的

    封装很多对象互相通信的方式,允许每个对象的行为互相不同。

    何时使用

    1. 对象间的通信很复杂
    2. 对象间的关系很复杂
    3. 需要公共控制中心

    常见使用场景

    1. 邮件列表需要统计谁在线,让发送邮件的人可以和在线的人通信,一旦非在线的人上线,则要再去通知。中介者在其中就可以处理这种关系。
    2. 机场的控制中心可以避免飞机之间互相直接联系,每架飞机都和控制中心通信,由控制中心决定谁可以降落。
    3. DOM树上绑定事件的时候,我们可以绑定在根节点,而不是绑定到单独的node,这样根节点就像一个Mediator。

    代码实现

    https://github.com/benhaben/essentialjsdesignpatterns.git

    下面的代码实现了一个聊天室,参与聊天的人需要互相通信,聊天室用来管理发送给谁。

    mediator
    
    
    var Participant = function(name) {
        this.name = name;
        this.chatroom = null;
    };
    Participant.prototype = {
        send: function(message, to) {
            this.chatroom.send(message, this, to);
        },
        receive: function(message, from) {
            log.add(from.name + " to " + this.name + ": " + message);
        }
    };
    var Chatroom = function() {
        var participants = {};
    
        return {
    
            register: function(participant) {
                participants[participant.name] = participant;
                participant.chatroom = this;
            },
    
            send: function(message, from, to) {
                if (to) {
                    to.receive(message, from);
                } else {
                    for (key in participants) {
                        if (participants[key] !== from) {
                            participants[key].receive(message, from);
                        }
                    }
                }
            }
        };
    };
    var log = (function() {
        var log = "";
    
        return {
            add: function(msg) { log += msg + "\n"; },
            show: function() { console.log(log); log = ""; }
        }
    })();
    
    function run() {
        var yoko = new Participant("Yoko");
        var john = new Participant("John");
        var paul = new Participant("Paul");
        var ringo = new Participant("Ringo");
        var chatroom = new Chatroom();
        chatroom.register(yoko);
        chatroom.register(john);
        chatroom.register(paul);
        chatroom.register(ringo);
        yoko.send("All you need is love.");
        yoko.send("I love you John.");
        john.send("Hey, no need to broadcast", yoko);
        paul.send("Ha, I heard that!");
        ringo.send("Paul, what do you think?", paul);
        log.show();
    }
    
    run();
    

    上面的机制可以利用事件来实现,js的世界中,emit事件才是更常见的写法。

    Event Aggregator Pattern

    enent aggregator

    事件聚合器模式和Mediator很像,都是集中式管理通信,却别在于中介者控制何时去通知,有一定的业务逻辑,对象之间也是相关的。而Event Aggregator只是发出事件,然后就忘记,中间层更轻。Event Aggregator适合对象间没有直接关系的情况。

    非直接的关系很适合用event aggregators,在现代应用中,视图需要互相通信的场景很常见,但是这些视图之间并没有关系。比如,一个菜单item响应click事件,需要改变内容区,但是我们并不想菜单持有内容区,这会导致难以维护的代码(想想很多视图区需要互相持有的情况)。这个时候,我们可以用事件聚合器触发一个“menu:click:foo” 事件,让“foo”对象去显示内容到内容视图。

    代码实现

    下面的例子让mediator监听event aggregator的事件,把两个模式集合起来观察。mediator收到事件后,可以控制相关的对象产生一些行为。但是mediator本身和事件发生者又没有关系。

    /**
     * Created by shenyin.sy on 17/3/17.
     */
    
    // MenuItem(菜单,事件聚合者)和MyWorkflow(中介者)并没有直接关系
    // 所以通过事件menu:click:XXX来通知,当MyWorkflow收到通知后,可以
    // 在doStuff中通知相关的对象,完成某些工作流程
    
    "use strict"
    
    
    
    const EventEmitter = require('events');
    
    
    class EventAggregator extends EventEmitter {
        constructor() {
            super();
            this.prefix = "menu:click:";
        }
        clickedIt(menuName){
            this.emit(this.prefix + menuName);
        }
    }
    
    var eventAggregator = new EventAggregator();
    
    //模拟一个菜单
    var MenuItem ={
    
        _menuItemName: "foo",
        get menuItemName() {
            return this._menuItemName;
        },
        set menuItemName(val) {
            this._menuItemName = val
        },
        clickedIt: function(e){
            console.log(`clickedIt : ${e}`);
            // assume this triggers "menu:click:foo"
            eventAggregator.emit("menu:click:" + this.menuItemName);
        }
    
    };
    // ... somewhere else in the app
    
    var MyWorkflow = function(){
        eventAggregator.on("menu:click:foo", this.doStuff.bind(this));
        this.tasks=[];
    };
    MyWorkflow.prototype.addTask = function(task){
        this.tasks.push(task);
    }
    
    MyWorkflow.prototype.doStuff = function(){
        // instantiate multiple objects here.
        // set up event handlers for those objects.
        // coordinate all of the objects into a meaningful workflow.
        for(let i = 0; i < this.tasks.length; i++){
            console.log(`do task : ${this.tasks[i]}`);
    
        }
    
    };
    
    function run() {
        //myWorkflow是一个中介者,他会通知task去做任务,并且只关心"menu:click:foo"事件
        var myWorkflow = new MyWorkflow();
        myWorkflow.addTask(1);
        myWorkflow.addTask(2);
        myWorkflow.addTask(3);
    
        //事件发生
        MenuItem.clickedIt("模拟点击菜单项");
        // MenuItem1.clickedIt("模拟点击菜单项1");
        // MenuItem2.clickedIt("模拟点击菜单项2");
    
    }
    
    run();
    

    总结

    中介者和事件聚合代码上的差别很细微,但是思想确实很远,设计模式实际上就是思想,所以明白这些差异很重要。另外传统的设计模式都是依赖接口,图表上也一般有接口,这对js来说复杂了,js实现设计模式可以说完全不需要接口,这也从反面说明,思想可以有各种实现方式。

    参考文章

    https://martinfowler.com/eaaDev/EventAggregator.html

    相关文章

      网友评论

        本文标题:JS设计模式3 - The Mediator Pattern,E

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