JavaScript设计模式:中介者模式详解与实战

作者:闭包旅人时间:2026-10-28 23:59:38浏览:626

本文深入解析JavaScript中的中介者模式,通过伪代码、消息总线及完整交互示例,展示如何利用中央控制对象封装模块交互以实现解耦。文章对比了中介者与观察者、外观模式的区别,并指出该模式适用于多对多复杂通信场景,同时提醒中介者对象自身可能变得复杂。

中介者模式概述

介绍

中介者模式(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中,中介者模式非常常见,它类似于观察者模式中的消息总线(Message 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>

总结

总结

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

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

作者最新文章