深入理解JavaScript系列(36):设计模式之中介者模式详解


Posted in Javascript onMarch 04, 2015

介绍

中介者模式(Mediator),用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

主要内容来自:http://www.addyosmani.com/resources/essentialjsdesignpatterns/book/#mediatorpatternjavascript

正文

软件开发中,中介者是一个行为设计模式,通过提供一个统一的接口让系统的不同部分进行通信。一般,如果系统有很多子模块需要直接沟通,都要创建一个中央控制点让其各模块通过该中央控制点进行交互。中介者模式可以让这些子模块不需要直接沟通,而达到进行解耦的目的。

打个比方,平时常见的机场交通控制系统,塔台就是中介者,它控制着飞机(子模块)的起飞和降落,因为所有的沟通都是从飞机向塔台汇报来完成的,而不是飞机之前相互沟通。中央控制系统就是该系统的关键,也就是软件设计中扮演的中介者角色。

我们先用伪代码来理解一下:

// 如下代码是伪代码,请不要过分在意代码

// 这里app命名空间就相当于扮演中介者的角色

var app = app || {};

 

// 通过app中介者来进行Ajax请求

app.sendRequest = function ( options ) {

    return $.ajax($.extend({}, options);

}

 

// 请求URL以后,展示View

app.populateView = function( url, view ){

  $.when(app.sendRequest({url: url, method: 'GET'})

     .then(function(){

         //显示内容

     });

}

 

// 清空内容

app.resetView = function( view ){

   view.html('');

}

在JavaScript里,中介者非常常见,相当于观察者模式上的消息Bus,只不过不像观察者那样通过调用pub/sub的形式来实现,而是通过中介者统一来管理,让我们在观察者的基础上来给出一个例子:
var mediator = (function () {

    // 订阅一个事件,并且提供一个事件触发以后的回调函数

    var subscribe = function (channel, fn) {

        if (!mediator.channels[channel]) mediator.channels[channel] = [];

        mediator.channels[channel].push({ context: this, callback: fn });

        return this;

    },
    // 广播事件

    publish = function (channel) {

        if (!mediator.channels[channel]) return false;

        var args = Array.prototype.slice.call(arguments, 1);

        for (var i = 0, l = mediator.channels[channel].length; i < l; i++) {

            var subscription = mediator.channels[channel][i];

            subscription.callback.apply(subscription.context, args);

        }

        return this;

    };
    return {

        channels: {},

        publish: publish,

        subscribe: subscribe,

        installTo: function (obj) {

            obj.subscribe = subscribe;

            obj.publish = publish;

        }

    };
} ());

调用代码,相对就简单了:
(function (Mediator) {
    function initialize() {
        // 默认值

        mediator.name = "dudu";
        // 订阅一个事件nameChange

        // 回调函数显示修改前后的信息

        mediator.subscribe('nameChange', function (arg) {

            console.log(this.name);

            this.name = arg;

            console.log(this.name);

        });

    }
    function updateName() {

        // 广播触发事件,参数为新数据

        mediator.publish('nameChange', 'tom'); // dudu, tom

    }
    initialize(); // 初始化

    updateName(); // 调用
})(mediator);

中介者和观察者

到这里,大家可能迷糊了,中介者和观察者貌似差不多,有什么不同呢?其实是有点类似,但是我们来看看具体的描述:

观察者模式,没有封装约束的单个对象,相反,观察者Observer和具体类Subject是一起配合来维护约束的,沟通是通过多个观察者和多个具体类来交互的:每个具体类通常包含多个观察者,而有时候具体类里的一个观察者也是另一个观察者的具体类。

而中介者模式所做的不是简单的分发,却是扮演着维护这些约束的职责。

中介者和外观模式

很多人可能也比较迷糊中介者和外观模式的区别,他们都是对现有各模块进行抽象,但有一些微妙的区别。

中介者所做的是在模块之间进行通信,是多向的,但外观模式只是为某一个模块或系统定义简单的接口而不添加额外的功能。系统中的其它模块和外观模式这个概念没有直接联系,可以认为是单向性。

再给出一个完整的例子:

<!doctype html>

<html lang="en">

<head>

    <title>JavaScript Patterns</title>

    <meta charset="utf-8">

</head>

<body>

<div id="results"></div>

    <script>

        function Player(name) {

            this.points = 0;

            this.name = name;

        }

        Player.prototype.play = function () {

            this.points += 1;

            mediator.played();

        };

        var scoreboard = {
            // 显示内容的容器

            element: document.getElementById('results'),
            // 更新分数显示

            update: function (score) {

                var i, msg = '';

                for (i in score) {

                    if (score.hasOwnProperty(i)) {

                        msg += '<p><strong>' + i + '<\/strong>: ';

                        msg += score[i];

                        msg += '<\/p>';

                    }

                }

                this.element.innerHTML = msg;

            }

        };
        var mediator = {
            // 所有的player

            players: {},
            // 初始化

            setup: function () {

                var players = this.players;

                players.home = new Player('Home');

                players.guest = new Player('Guest');

            },
            // play以后,更新分数

            played: function () {

                var players = this.players,

                    score = {

                        Home: players.home.points,

                        Guest: players.guest.points

                    };
                scoreboard.update(score);

            },
            // 处理用户按键交互

            keypress: function (e) {

                e = e || window.event; // IE

                if (e.which === 49) { // 数字键 "1"

                    mediator.players.home.play();

                    return;

                }

                if (e.which === 48) { // 数字键 "0"

                    mediator.players.guest.play();

                    return;

                }

            }

        };
        // go!

        mediator.setup();

        window.onkeypress = mediator.keypress;
        // 30秒以后结束

        setTimeout(function () {

            window.onkeypress = null;

            console.log('Game over!');

        }, 30000);

    </script>

</body>

</html>

总结

中介者模式一般应用于一组对象已定义良好但是以复杂的方式进行通信的场合,一般情况下,中介者模式很容易在系统中使用,但也容易在系统里误用,当系统出现了多对多交互复杂的对象群时,先不要急于使用中介者模式,而是要思考一下是不是系统设计有问题。

另外,由于中介者模式把交互复杂性变成了中介者本身的复杂性,所以说中介者对象会比其它任何对象都复杂。

Javascript 相关文章推荐
Jqyery中同等与js中windows.onload的应用
May 10 Javascript
JQuery制作的放大效果的popup对话框(未添加任何jquery plugin)分享
Apr 28 Javascript
JS中FRAME的操作问题实例分析
Oct 21 Javascript
JavaScript设计模式之抽象工厂模式介绍
Dec 28 Javascript
javascript实现验证身份证号的有效性并提示
Apr 30 Javascript
简单封装js的dom查询实例代码
Jul 08 Javascript
js正则表达式最长匹配(贪婪匹配)和最短匹配(懒惰匹配)用法分析
Dec 27 Javascript
jQuery插件zTree实现获取一级节点数据的方法
Mar 08 Javascript
jQuery复合事件用法示例
Jun 10 jQuery
p5.js入门教程之键盘交互
Mar 19 Javascript
浅谈鸿蒙 JavaScript GUI 技术栈
Sep 17 Javascript
vue缓存之keep-alive的理解和应用详解
Nov 02 Javascript
百度UEditor编辑器如何关闭抓取远程图片功能
Mar 03 #Javascript
jQuery实现复选框成对选择及对应取消的方法
Mar 03 #Javascript
js实现文本框中输入文字页面中div层同步获取文本框内容的方法
Mar 03 #Javascript
JS实现文字放大效果的方法
Mar 03 #Javascript
jQuery实现的感应鼠标悬停图片色彩渐显效果
Mar 03 #Javascript
js给网页加上背景音乐及选择音效的方法
Mar 03 #Javascript
分享一则JavaScript滚动条插件源码
Mar 03 #Javascript
You might like
骨王战斗力在公会成员中排不进前五,却当选了会长,原因竟是这样
2020/03/02 日漫
《OVERLORD》第四季,终于等到你!
2020/03/02 日漫
ThinkPHP函数详解之M方法和R方法
2015/09/10 PHP
PHP扩展开发教程(总结)
2015/11/04 PHP
[原创]php简单防盗链验证实现方法
2016/07/09 PHP
PHP读取CSV大文件导入数据库的实例
2017/07/24 PHP
js制作的鼠标悬浮时产生的下拉框效果
2012/10/27 Javascript
Javascript改变CSS样式(局部和全局)
2013/12/18 Javascript
JavaScript获取当前页面上的指定对象示例代码
2014/02/28 Javascript
jQuery对象的length属性用法实例
2014/12/27 Javascript
jQuery中:enabled选择器用法实例
2015/01/04 Javascript
jquery带下拉菜单和焦点图代码分享
2015/08/24 Javascript
Vue.js计算属性computed与watch(5)
2016/12/09 Javascript
vue.js+Element实现表格里的增删改查
2017/01/18 Javascript
nodejs socket实现的服务端和客户端功能示例
2017/06/02 NodeJs
angular中实现li或者某个元素点击变色的两种方法
2017/07/27 Javascript
react native 原生模块桥接的简单说明小结
2019/02/26 Javascript
微信小程序整合使用富文本编辑器的方法详解
2019/04/25 Javascript
layui checkbox默认选中,获取选中值,清空所有选中项的例子
2019/09/02 Javascript
基于JavaScript 实现拖放功能
2019/09/12 Javascript
vue 查看dist文件里的结构(多种方式)
2020/01/17 Javascript
在antd中setFieldsValue和defaultVal的用法
2020/10/29 Javascript
[01:46]2020完美世界全国高校联赛秋季赛报名开启
2020/10/15 DOTA
[41:21]夜魇凡尔赛茶话会 第三期02:看图识人
2021/03/11 DOTA
[08:08]DOTA2-DPC中国联赛2月28日Recap集锦
2021/03/11 DOTA
wxPython的安装与使用教程
2018/08/31 Python
python爬虫 猫眼电影和电影天堂数据csv和mysql存储过程解析
2019/09/05 Python
解决pandas展示数据输出时列名不能对齐的问题
2019/11/18 Python
CSS3实现多重边框的方法总结
2016/05/31 HTML / CSS
iHerb台湾:维生素、保健品和健康产品
2018/01/31 全球购物
JRE、JDK、JVM之间的关系怎样
2012/05/16 面试题
外包公司软件测试工程师
2014/11/01 面试题
大学生涯自我鉴定
2014/01/16 职场文书
2014年保卫部工作总结
2014/11/21 职场文书
合理化建议书范文
2015/09/14 职场文书
再次探讨go实现无限 buffer 的 channel方法
2021/06/13 Golang