## 介紹
中介者模式(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>
~~~
## 總結
中介者模式一般應用于一組對象已定義良好但是以復雜的方式進行通信的場合,一般情況下,中介者模式很容易在系統中使用,但也容易在系統里誤用,當系統出現了多對多交互復雜的對象群時,先不要急于使用中介者模式,而是要思考一下是不是系統設計有問題。
另外,由于中介者模式把交互復雜性變成了中介者本身的復雜性,所以說中介者對象會比其它任何對象都復雜。
- (1)編寫高質量JavaScript代碼的基本要點
- (2)揭秘命名函數表達式
- (3)全面解析Module模式
- (4)立即調用的函數表達式
- (5)強大的原型和原型鏈
- (6)S.O.L.I.D五大原則之單一職責SRP
- (7)S.O.L.I.D五大原則之開閉原則OCP
- (8)S.O.L.I.D五大原則之里氏替換原則LSP
- (9)根本沒有“JSON對象”這回事!
- (10)JavaScript核心(晉級高手必讀篇)
- (11)執行上下文(Execution Contexts)
- (12)變量對象(Variable Object)
- (13)This? Yes, this!
- (14)作用域鏈(Scope Chain)
- (15)函數(Functions)
- (16)閉包(Closures)
- (17)面向對象編程之一般理論
- (18)面向對象編程之ECMAScript實現
- (19)求值策略
- (20)《你真懂JavaScript嗎?》答案詳解
- (21)S.O.L.I.D五大原則之接口隔離原則ISP
- (22)S.O.L.I.D五大原則之依賴倒置原則DIP
- (23)JavaScript與DOM(上)——也適用于新手
- (24)JavaScript與DOM(下)
- (25)設計模式之單例模式
- (26)設計模式之構造函數模式
- (27)設計模式之建造者模式
- (28)設計模式之工廠模式
- (29)設計模式之裝飾者模式
- (30)設計模式之外觀模式
- (31)設計模式之代理模式
- (32)設計模式之觀察者模式
- (33)設計模式之策略模式
- (34)設計模式之命令模式
- (35)設計模式之迭代器模式
- (36)設計模式之中介者模式
- (37)設計模式之享元模式
- (38)設計模式之職責鏈模式
- (39)設計模式之適配器模式
- (40)設計模式之組合模式
- (41)設計模式之模板方法
- (42)設計模式之原型模式
- (43)設計模式之狀態模式
- (44)設計模式之橋接模式
- (45)代碼復用模式(避免篇)
- (46)代碼復用模式(推薦篇)
- (47)對象創建模式(上篇)
- (48)對象創建模式(下篇)
- (49)Function模式(上篇)
- (50)Function模式(下篇)
- (結局篇)