JS设计模式3 - The Mediator Pattern,Event Aggregator patterns

中介者模式

中介者模式

目的

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

何时使用

  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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容

  • 1 场景问题# 1.1 如果没有主板## 大家都知道,电脑里面各个配件之间的交互,主要是通过主板来完成的(事实上主...
    七寸知架构阅读 2,159评论 0 56
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,577评论 18 399
  • 单例模式 适用场景:可能会在场景中使用到对象,但只有一个实例,加载时并不主动创建,需要时才创建 最常见的单例模式,...
    Obeing阅读 2,054评论 1 10
  • 这两天,北京地铁里男子骂人的视频广为流传。 视频中,两个女性扫码创业者多次要求男子扫二维码,男子拒绝并愤怒了,张口...
    张小炘阅读 313评论 1 1
  • 你好哇,18岁的雪雪,我是19岁的小白~ 第一次见面,我说〔好漂亮〕,你说〔说我么〕哈哈哈,我这个“搬迁户”是觉得...
    陀陀刺猬阅读 337评论 1 0