深入理解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 12 Javascript
THREE.JS入门教程(6)创建自己的全景图实现步骤
Jan 25 Javascript
document.compatMode的CSS1compat使用介绍
Apr 03 Javascript
原生javascript实现分页效果
Apr 21 Javascript
AngularJS基于provider实现全局变量的读取和赋值方法
Jun 28 Javascript
微信小程序中button组件的边框设置的实例详解
Sep 27 Javascript
js中bool值的转换及“&amp;&amp;”、“||”、 “!!”详解
Dec 21 Javascript
vue.js中实现登录控制的方法示例
Apr 23 Javascript
Nuxt配合Node在实际生产中的应用详解
Aug 07 Javascript
vue3.0 CLI - 3.2 路由的初级使用教程
Sep 20 Javascript
vue中watch和computed为什么能监听到数据的改变以及不同之处
Dec 27 Javascript
微信小程序基础教程之echart的使用
Jun 01 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
CodeIgniter php mvc框架 中国网站
2008/05/26 PHP
基于php缓存的详解
2013/05/15 PHP
深入解读php中关于抽象(abstract)类和抽象方法的问题分析
2014/01/03 PHP
网页上facebook分享功能具体实现
2014/01/26 PHP
php判断是否连接上网络的方法实例详解
2016/12/14 PHP
php自定义函数实现统计中文字符串长度的方法小结
2017/04/15 PHP
ThinkPHP5&amp;5.1实现验证码的生成、使用及点击刷新功能示例
2020/02/07 PHP
JsEasy简介 JsEasy是什么?与下载
2007/03/07 Javascript
JavaScript 使用技巧精萃(.net html
2009/04/25 Javascript
基于JQuery的密码强度验证代码
2010/03/01 Javascript
JS自定义功能函数实现动态添加网址参数修改网址参数值
2013/08/02 Javascript
jQuery中关于ScrollableGridPlugin.js(固定表头)插件的使用逐步解析
2014/07/17 Javascript
JavaScript继承基础讲解(原型链、借用构造函数、混合模式、原型式继承、寄生式继承、寄生组合式继承)
2014/08/16 Javascript
node.js中的fs.writeSync方法使用说明
2014/12/15 Javascript
利用JavaScript的AngularJS库制作电子名片的方法
2015/06/18 Javascript
jQuery动态生成Bootstrap表格
2016/11/01 Javascript
解析AngularJS中get请求URL出现的跨域问题
2016/12/01 Javascript
angularJs中datatable实现代码
2017/06/03 Javascript
基于angular-utils-ui-breadcrumbs使用心得(分享)
2017/11/03 Javascript
用POSTMAN发送JSON格式的POST请求示例
2018/09/04 Javascript
Vue通过for循环随机生成不同的颜色或随机数的实例
2019/11/09 Javascript
基于element-ui封装表单金额输入框的方法示例
2021/01/06 Javascript
[54:54]Newbee vs Serenity 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/18 DOTA
python中文编码问题小结
2014/09/28 Python
使用Python实现一个栈判断括号是否平衡
2018/08/23 Python
Python3.0 实现决策树算法的流程
2019/08/08 Python
python和go语言的区别是什么
2020/07/20 Python
html5时钟实现代码
2010/10/22 HTML / CSS
英国最大的化装舞会服装网站:Fancydress.com
2017/08/15 全球购物
在校学生职业规划范文
2014/01/08 职场文书
拾金不昧表扬信范文
2014/01/11 职场文书
服装发布会策划方案
2014/05/22 职场文书
代领学位证书毕业证书委托书
2014/09/30 职场文书
见习报告怎么写
2014/10/31 职场文书
2014年小学辅导员工作总结
2014/12/23 职场文书
Python scrapy爬取起点中文网小说榜单
2021/06/13 Python