Skip to main content

Observer Pattern

前言

觀察者模式大家應該都不太陌生, 很多程式語言都已經有interface或是已經實踐observable的class了, 例如Java和javascript中的Rxjs等等。

介紹

我們用現在最流行的Youtube來解釋觀察者模式,Youtube上的訂閱方式如下

  • 觀眾對喜歡的youtuber按下訂閱按鈕
  • 當youtuber有新影片上架時,會主動通知有訂閱的觀眾
  • 一位youtuber可以同時被多位觀眾訂閱
  • 觀眾能隨時取消訂閱,也就不會收到youtuber的新影片通知

例子

  • 我們模擬上面的例子
  • Subject interface
interface Subject {
registerObserver(observer: Observer): void;
removeObserver(observer: Observer):void;
notifyObservers():void;
}

  • Observer
interface Observer {
update(newVideo: object): void;
}
  • Video: 這邊只是簡單寫個title
interface Video {
title: string
}
  • Youtuber
class Youtuber implements Subject {
observers: Array<Observer>;
video: Video;

constructor() {
this.observers = [];
this.video = { title: 'init' };
}

registerObserver(observer: Observer) {
this.observers.push(observer);
}

removeObserver(observer: Observer) {
const index = this.observers.indexOf(observer);
this.observers = this.observers.splice(index, 1);
}

notifyObservers() {
this.observers.forEach(observer => {
observer.update(this.video);
});
}

setVideo() {
this.video = { title: new Date().getTime().toString()};
this.notifyObservers();
}
}
  • User
class User implements Observer {
subject: Subject;
constructor(subject: Subject) {
this.subject = subject;
this.subject.registerObserver(this);
}

update(video: Video) {
this.display(video);
}

unsubscribe() {
this.subject.removeObserver(this);
}

display(video: Video) {
console.log('video:' + video.title);
}
}
  • Main
const youtuber = new Youtuber();
const user = new User(youtuber);
const user2 = new User(youtuber);
const user3 = new User(youtuber);
youtuber.setVideo();

user2.unsubscribe();
console.log('user2 unsubscribe');
user3.unsubscribe();
console.log('user3 unsubscribe');
youtuber.setVideo();
  • Result
TypeScript v3.3.3 linux/amd64
video:1563710179580
video:1563710179580
video:1563710179580
user2 unsubscribe
user3 unsubscribe
video:1563710179581
  • 可以由結果看到一開始使用者都訂閱了youtuber, 所以當youtuber上架新影片時(youtuber.setVideo()), 三個user都收到了通知(video:1563710179580), 接著user2, user3退訂了之後, youtuber就只發通知給訂閱中(沒退訂)的user而已

優點

  • 完美的切割程式碼中會變動的部分,將固定與會變動的部分分開
  • 定義了通知的傳遞機制
  • 讓物件與物件間達到鬆散 (loose coupling)

結論

  • 我們這次介紹的是Push Model,另外還有Pull Model下次有時間再介紹,但基本上概念是相同的,差別僅在於Observer讀取的方式而已。

Source Code

https://github.com/YiTingLee/Design-Pattern/blob/main/observer-pattern/index.ts