觀察者模式(Observer Pattern)
觀察者模式是一種物件間的一對多依賴關係,當一個物件的狀態發生變化時,所有依賴他的物件都將得到通知。通常運用在物件之間的訊息通訊中。
// 比如現在有一群學生,可以組成小組,可以尋求幫助
class Students {
constructor(name, level) {
this.name = name;
this.level = level;
this.tasks = [];
}
askForHelp(subject) {
console.log(`關於${subject}, ${this.level}${this.name}向大家尋求幫助!`)
this.tasks.forEach((fn) => {
fn(subject);
});
}
makeTeam(student, fn) {
console.log(`${this.level}${this.name}與${student.level}${student.name}組成了學習小組!`);
student.tasks.push(fn);
}
}
const studentWango = new Students('Wango', '學神');
const studentLily = new Students('Lily', '學霸');
const studentTom = new Students('Tom', '平平無奇的');
const studentPeter = new Students('Peter', '學渣');
// 老師說Peter的成績比較差,所以大家都和Peter組成了學習小組
studentWango.makeTeam(studentPeter, function(subject) {
console.log(`Wango說:${subject}確實很難,我也花了好幾分鐘複習才堪堪得到了滿分!`);
})
// 學神Wango與學渣Peter組成了學習小組!
studentLily.makeTeam(studentPeter, function(subject) {
console.log(`Lily說:${subject}!? 背就完事了!`);
})
// 學霸Lily與學渣Peter組成了學習小組!
studentTom.makeTeam(studentPeter, function(subject) {
console.log(`Tom說:${subject}的話,一定要好好學,但學不好也沒事,我們都是凡人!`);
})
// 平平無奇的Tom與學渣Peter組成了學習小組!
// 有一天...
studentPeter.askForHelp('英語');
// 關於英語, 學渣Peter向大家尋求幫助!
// Wango說:英語確實很難,我也花了好幾分鐘複習才堪堪得到了滿分!
// Lily說:英語!? 背就完事了!
// Tom說:英語的話,一定要好好學,但學不好也沒事,我們都是凡人!
釋出訂閱模式(Pub-Sub Pattern)
釋出訂閱模並不在24
種設計模式之中,只是觀察者模式的一種變體。相比觀察者模式,訂閱釋出模式新增了一個任務排程中心,訂閱者將自己想要訂閱的事件註冊到排程中心,當釋出者釋出該事件到排程中心,也就是觸發該事件時,由排程中心同一排程訂閱者註冊到排程中心的程式碼。
// 同樣的學習小組
class Students {
constructor(name, level) {
this.name = name;
this.level = level;
}
// 訂閱事件
on(type, fn) {
ClassRoom.subscribe(type, fn);
}
// 釋出內容
emit(type, content) {
ClassRoom.askForHelp(type, content);
}
}
// 任務排程中心
class ClassRoom {
static topic = Object.create(null)
// 對於某個事件釋出任務
static askForHelp(type, content) {
console.log(`${type}:${content}`);
if (!ClassRoom.topic[type] || ClassRoom.topic[type].length === 0) {
console.log(`然而並沒有學生對${type}感興趣!`);
return;
}
ClassRoom.topic[type].forEach((fn) => {
fn(content);
});
}
// 訂閱某個型別的事件
static subscribe(type, fn) {
if (!ClassRoom.topic[type]) {
ClassRoom.topic[type] = [];
}
console.log(`有學生訂閱了${type}.`);
ClassRoom.topic[type].push(fn);
}
}
const studentWango = new Students('Wango', '學神');
const studentLily = new Students('Lily', '學霸');
const studentTom = new Students('Tom', '平平無奇的');
const studentPeter = new Students('Peter', '學渣');
studentWango.on('Life', function(type, content) {
console.log(`Wango說: 終於有人問生活上的問題了,果然還是做個凡人比較快樂啊!`);
})
// 有學生訂閱了Life.
studentWango.on('English', function(type, content) {
console.log(`Wango說:英語確實很難,我也花了好幾分鐘複習才堪堪得到了滿分!`);
})
// 有學生訂閱了English.
studentTom.on('English', function(type, content) {
console.log(`Tom說:英語的話,一定要好好學,但學不好也沒事,我們都是凡人!`);
})
// 有學生訂閱了English.
studentLily.on('English', function(type, content) {
console.log(`Lily說: 英語?! 背就完事了!`);
})
// 有學生訂閱了English.
studentLily.emit('Life', '這個世界有英雄嗎?');
// Life:這個世界有英雄嗎?
// Wango說: 終於有人問生活上的問題了,果然還是做個凡人比較快樂啊!
studentTom.emit('English', '單詞記不住怎麼辦啊?');
// English:單詞記不住怎麼辦啊?
// Wango說:英語確實很難,我也花了好幾分鐘複習才堪堪得到了滿分!
// Tom說:英語的話,一定要好好學,但學不好也沒事,我們都是凡人!
// Lily說: 英語?! 背就完事了!
studentPeter.emit('Game', '學什麼?玩遊戲啊!');
// Game:學什麼?玩遊戲啊!
// 然而並沒有學生對Game感興趣!
區別與聯絡
相比觀察者模,訂閱釋出模式中的的訂閱者和釋出者分離更加徹底。訂閱者在訂閱的時候只關注事件本身,而不用關心是誰釋出的資訊;而釋出者同樣只需要關注釋出的內容,不用關心是誰在訂閱,如何處理。
總的來說,儘管訂閱釋出模式多了個排程中心,但使用起來可以更加靈活,對多事件的支援更好,當然,記憶體消耗更大,畢竟要為維護多個事件佇列。