Frond-End程序员开源工具技巧

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

2017-03-17  本文已影响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

上一篇下一篇

猜你喜欢

热点阅读