代理模式的意義是用一個物件做為另一個物件的介面,主要的目的是為了保護物件的存取,避免不正確的操作行為。
var car = {
start: function() {console.log("發動");},
stop: function() {console.log("關閉");},
run: function() {console.log("加速");}
};
function ProxyCar(car) {
var isStart = false;
this.start = function() {
if (isStart) {
console.log("引擎已經發動");
} else {
car.start();
isStart = true;
}
};
this.stop = function() {
if (!isStart) {
console.log("引擎已經關閉");
} else {
car.stop();
isStart = false;
}
};
this.run = function() {
if (!isStart) {
console.log("尚未發動引擎");
} else {
car.run();
}
};
}
var proxyCar = new ProxyCar(car);
proxyCar.run(); //尚未發動引擎
proxyCar.stop(); //引擎已經關閉
proxyCar.start(); //發動
proxyCar.run(); //加速
生活中當電梯未停妥時無法開啟電梯門,或電梯門尚未關閉時無法移動到其他樓層。
優點
缺點
當物件和物件之間或有錯綜複雜的交互作用,可將這些關係交由另一物件 (中介者) 來處理,來減少這些物件間的耦合。
function Player(name) {
this.points = 0;
this.name = name;
}
Player.prototype.play = function() {
this.points += 1;
mediator.played();
};
var scoreboard = {
el: document.body,
update: function(score) {
var msg = "";
for (var i in score) {
if (score.hasOwnProperty(i)) {
msg += "<p><b>" + i + "</b>:";
msg += score[i];
msg += "</p>";
}
}
this.el.innerHTML = msg;
}
};
有時候朋友間打牌,要自己紀錄與每個人間的分數關係,當人數越多時就會越複雜,因此如果能有一個計分板,將每個人得分都更新到計分板上,就能更輕鬆一些。
var mediator = {
users: {},
init: function() {
this.users.home = new Player("Home");
this.users.guest = new Player("Guest");
},
played: function() {
var score = {
Home: this.users.home.points,
Guest: this.users.guest.points
};
scoreboard.update(score);
},
keypress: function(e) {
if (e.keyCode === 48) {
mediator.users.home.play();
return;
}
if (e.keyCode === 49) {
mediator.users.guest.play();
return;
}
}
};
mediator.init();
window.onkeypress = mediator.keypress;
下面的範例簡化了在新增 Player 時的操作,只要透過中介者加入新的 player 即可。
function Player(name) {
this.points = 0;
this.name = name;
}
Player.prototype.play = function() {
this.points += 1;
mediator.played();
};
var scoreboard = {
el: document.body,
update: function(score) {
var msg = "";
for (var i in score) {
if (score.hasOwnProperty(i)) {
msg += "<p><b>" + i + "</b>:";
msg += score[i];
msg += "</p>";
}
}
this.el.innerHTML = msg;
}
};
var mediator = {
users: {},
add: function(key, obj) {
this.users[obj.name] = {
key: key,
obj: obj
};
},
played: function() {
var score = {};
for (var user in this.users) {
score[user] = this.users[user].obj.points;
}
scoreboard.update(score);
},
keypress: function(e) {
for (var user in mediator.users) {
user = mediator.users[user];
if (e.keyCode === user.key) {
user.obj.play();
return;
}
}
}
};
mediator.add(48, new Player("Home"));
mediator.add(49, new Player("Guest"));
mediator.add(50, new Player("Other"));
mediator.add(51, new Player("Reynold"));
mediator.add(52, new Player("Cher"));
window.onkeypress = mediator.keypress;
優點
缺點
觀察者模式又可以稱為訂閱模式,是一對多的關係,讓多個觀察者同時監聽一個主題的事件,當這個主題物件狀態發生改變就會通知觀察者。
而大家熟知的 JavaScript 事件就是屬於觀察者模式。
var messageCenter = {
events: {},
emit: function(type, message) {
if (this.events[type]) {
for (var i = 0; i < this.events[type].length; i++) {
this.events[type][i].callback(message);
}
}
},
on: function(obj, type, callback) {
this.events[type] = this.events[type] || [];
this.events[type].push({
obj: obj,
callback: callback
});
},
off: function(obj, type) {
if (this.events[type]) {
for (var i = 0; i < this.events[type].length; i++) {
if (this.events[type][i].obj === obj) {
this.events[type].splice(i,1);
i--;
}
}
}
}
};
function User(messageCenter) {
this.messageCenter = messageCenter;
}
User.prototype.addEvent = function(type, callback) {
this.messageCenter.on(this, type, callback);
};
User.prototype.removeEvent = function(type) {
this.messageCenter.off(this, type);
};
var UserA = new User(messageCenter);
UserA.addEvent("todo", function(msg) {
console.log("UserA todo:" + msg);
});
UserA.addEvent("test", function(msg) {
console.log("UserA test:" + msg);
});
var UserB = new User(messageCenter);
UserB.addEvent("todo", function(msg) {
console.log("UserB todo:" + msg);
});
var UserC = new User(messageCenter);
UserC.addEvent("test", function(msg) {
console.log("UserC test:" + msg);
});
messageCenter.emit("todo", "News");
messageCenter.emit("test", "Word");
生活中如果我們想看報紙,則可以打電話到報社去訂閱,這樣每天早上就會收到報紙,若不想看的話,也只要致電過去退訂即可。
若讀者有聽過 Rx.js ,其就是使用觀察者模式為基礎的程式庫。
優點
缺點
對於設計模式的簡介這邊暫時告一個段落了,希望看完了這三天的文章,能對這些設計模式有個基礎的概念。
參考資料:
Tommy - 深入 JavaScript 核心課程
程式前沿 - 設計模式 – 觀察者模式