深入理解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 相关文章推荐
写js时遇到的一些小问题
Dec 06 Javascript
使用jQuery快速解决input中placeholder值在ie中无法支持的问题
Jan 02 Javascript
原生javascript模仿win8等待提示圆圈进度条
Apr 24 Javascript
微信JS接口汇总及使用详解
Jan 09 Javascript
JavaScript实现表格点击排序的方法
May 11 Javascript
Bootstrap每天必学之简单入门
Nov 19 Javascript
JQuery给select添加/删除节点的实现代码
Apr 26 Javascript
html+js实现简单的计算器代码(加减乘除)
Jul 12 Javascript
javascript实现二叉树的代码
Jun 08 Javascript
详解webpack3如何正确引用并使用jQuery库
Aug 26 jQuery
JS获取今天是本月第几周、本月共几周、本月有多少天、是今年的第几周、是今年的第几天的示例代码
Dec 05 Javascript
微信小程序中的上拉、下拉菜单功能
Mar 13 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
使用PHP socke 向指定页面提交数据
2008/07/23 PHP
深入php socket的讲解与实例分析
2013/06/13 PHP
php中操作memcached缓存进行增删改查数据的实现代码
2014/08/15 PHP
php中magic_quotes_gpc对unserialize的影响分析
2014/12/16 PHP
ThinkPHP实现分页功能
2017/04/28 PHP
PHP中使用jQuery+Ajax实现分页查询多功能操作(示例讲解)
2017/09/17 PHP
PHP排序算法之冒泡排序(Bubble Sort)实现方法详解
2018/04/20 PHP
laravel实现上传图片并在页面显示的例子
2019/10/14 PHP
JavaScript 定义function的三种方式小结
2009/10/16 Javascript
DIV外区域Click后关闭DIV的实现代码
2011/12/21 Javascript
js判断是否为ie的方法小结
2014/01/13 Javascript
javascript的函数作用域
2014/11/12 Javascript
Jquery中$.post和$.ajax的用法小结
2015/04/28 Javascript
jQuery使用$.ajax进行即时验证的方法
2015/12/08 Javascript
jQuery实现放大镜效果实例代码
2016/03/17 Javascript
javascript数组常用方法汇总
2016/09/10 Javascript
判断jQuery是否加载完成,没完成继续判断的解决方法
2017/12/06 jQuery
vue 组件 全局注册和局部注册的实现
2018/02/28 Javascript
vue  elementUI 表单嵌套验证的实例代码
2019/11/06 Javascript
Vue中用JSON实现刷新界面不影响倒计时
2020/10/26 Javascript
Python实现数通设备端口使用情况监控实例
2015/07/15 Python
PyCharm代码回滚,恢复历史版本的解决方法
2018/10/22 Python
浅谈Python中eval的强大与危害
2019/03/13 Python
一行python实现树形结构的方法
2019/08/09 Python
python web框架Flask实现图形验证码及验证码的动态刷新实例
2019/10/14 Python
python实现在线翻译功能
2020/03/03 Python
4行Python代码生成图像验证码(2种)
2020/04/07 Python
详解python模块pychartdir安装及导入问题
2020/10/22 Python
HTML5中Localstorage的使用教程
2015/07/09 HTML / CSS
师范生自荐信范文
2013/10/06 职场文书
人力资源主管的岗位职责
2014/03/15 职场文书
小学生爱国演讲稿
2014/04/25 职场文书
2015年度优秀员工推荐信
2015/03/23 职场文书
2015年医院科室工作总结范文
2015/05/26 职场文书
2016年“七一建党节”广播稿
2015/12/18 职场文书
云服务器部署 Web 项目的实现步骤
2022/06/28 Servers