簡介
觀察者模式(Observer Pattern)是一種行為型模式。它定義對象間的一種一對多的依賴關系,當一個對象的狀態發生改變時,所有依賴于它的對象都得到通知并被自動更新。
觀察者模式使用三個類Subject、Observer和Client。Subject對象帶有綁定觀察者到Client對象和從Client對象解綁觀察者的方法。我們創建Subject類、Observer抽象類和擴展了抽象類Observer的實體類。
作用
- 一個對象狀態更新,其他依賴對象收到通知和自動更新的機制。
- 實現模塊化分離,實現主題與觀察者交互對象之間的松耦合。
1)觀察者定義了對象之間一對多的關系。
2)被觀察者(主題)用一個共同的接口來更新觀察者。
3)觀察者和被觀察者用松耦合方式結合,被觀察者不知道觀察者的細節,只知道觀察者實現了觀察者接口。
實現步驟
- 創建觀察者observer基礎接口,包含主題和更新方法
- 創建主題subject抽象類,包含observer列表以及添加和刪除方法
- 創建具體的主題類,實現通知方法,發布通知時輪詢通知全部觀察者
- 創建多個具體觀察者,與主題關聯,并實現自己的更新方法
- 客戶調用時先聲明主題,再將觀察者分別添加到主題,當主題發布通知時,觀察者自動更新
UML
?

Java代碼
觀察者接口
| // ObserverAPI.java 觀察者接口,Java 9已經默認支持Observer接口 |
| // 這里避免沖突采取ObserverAPI命名 |
| public interface ObserverAPI { |
| public Subject subject = null; |
| public void update(String content); |
| } |
具體觀察者
| // ConcreteObserver.java 具體的觀察者實現類,也可以看成訂閱者,關聯對應的主題類。 |
| // 不同的觀察者也可以對應多個主題 |
| public class ConcreteObserver implements ObserverAPI { |
| |
| public Subject subject; |
| |
| // 給觀察者綁定主題,同時把觀察者添加到主題列表 |
| public ConcreteObserver(Subject subject) { |
| this.subject = subject; |
| this.subject.register((ObserverAPI) this); |
| } |
| |
| // 觀察者發出更新通知,不用單獨告訴訂閱者,由訂閱者自行監聽 |
| public void update(String content) { |
| System.out.println(String.format("%s::update() [subject.name = %s content = %s]", |
| this.getClass().getName(), |
| this.subject.getName(), content)); |
| } |
| } |
| // ConcreteObserver2.java 具體的觀察者實現類,也可以看成訂閱者,關聯對應的主題類。 |
| // 不同的觀察者可以對應不同的主題。 |
| public class ConcreteObserver2 implements ObserverAPI { |
| |
| // 這里沒有在構造器就綁定某個主題,而是從客戶角度去注冊觀察者 |
| public ConcreteObserver2() { |
| } |
| |
| // 觀察者發出更新通知,觀察者自行監聽 |
| public void update(String content) { |
| System.out.println(String.format("%s::update() [content = %s]", |
| this.getClass().getName(), content)); |
| } |
| } |
抽象主題類
| // Subject.java 定義抽象主題類或者接口,供具體主題類繼承 |
| public abstract class Subject { |
| private String name; |
| // protected Set<ObserverAPI> observers = new HashSet<>(); |
| protected List<ObserverAPI> observers = new ArrayList<>(); |
| |
| public String getName() { |
| return name; |
| } |
| |
| public void setName(String name) { |
| this.name = name; |
| } |
| |
| public void register(ObserverAPI observer) { |
| System.out.println(this.getClass().getName() + "::register() [observer = " + observer.getClass().getSimpleName() + "]"); |
| observers.add(observer); |
| } |
| |
| public void remove(ObserverAPI observer) { |
| observers.remove(observer); |
| } |
| |
| // 通知由具體類來實現邏輯 |
| public abstract void notify(String name); |
| } |
具體主題類
| // ConcreteSubject.java 觀察者主題類,也是發布者,重寫具體的通知方法。不同主題可以關聯不同的觀察者。 |
| public class ConcreteSubject extends Subject { |
| |
| public ConcreteSubject(String name) { |
| this.setName(name); |
| } |
| |
| // 不同的主題類有自己的通知方法,批量通知綁定的觀察者 |
| @Override |
| public void notify(String content) { |
| System.out.println(this.getClass().getName() + "::notify() [content = " + content + "]"); |
| for (Object observer : this.observers) { |
| ((ObserverAPI) observer).update(content); |
| } |
| |
| } |
| |
| } |
測試調用
| /** |
| * 觀察者模式應用非常廣泛,主要是觀察者提前綁定到發布者 |
| * 當發布者發布消息時,批量廣播通知,而無需逐一通知 |
| * 觀察者監聽到消息后自己決定采取哪一種行為 |
| */ |
| |
| // 定義一個主題,也就是發布者 |
| Subject concreteSubject = new ConcreteSubject("subject1"); |
| // 再聲明觀察者,通過構造器注冊到主題上 |
| ObserverAPI observer1 = new ConcreteObserver(concreteSubject); |
| |
| // 也可以單獨給主題注冊一個新的觀察者 |
| concreteSubject.register(new ConcreteObserver2()); |
| // 可以移除觀察者對象,可以打開注釋試下 |
| // concreteSubject.remove(observer1); |
| |
| // 主題開始發布新通知,各觀察者自動更新 |
| concreteSubject.notify("hello, this is broadcast."); |
| |
Python代碼
觀察者接口
| # ObserverAPI.py 觀察者抽象父類,定義一些公共方法 |
| class ObserverAPI: |
| |
| def __init__(self, name): |
| self.name = name |
| |
| # 觀察者發出更新通知,觀察者自行監聽 |
| def update(self, content): |
| print(self.__class__.__name__ + '::update() [content = ' + content + ']') |
| |
| def set_name(self, name): |
| self.name = name |
具體觀察者
| # ConcreteObserver.py 具體的觀察者實現類,也可以看成訂閱者,關聯對應的主題類。 |
| # 不同的觀察者也可以對應多個主題 |
| from src.ObserverAPI import ObserverAPI |
| |
| # 具體的觀察者實現類,也可以看成訂閱者,關聯對應的主題類。 |
| # 不同的觀察者也可以對應多個主題 |
| |
| |
| class ConcreteObserver(ObserverAPI): |
| # 給觀察者綁定主題,同時把觀察者添加到主題列表 |
| def __init__(self, subject, name): |
| ObserverAPI.__init__(self, name) |
| |
| # python3支持的父類調用 |
| # super(ConcreteObserver, self).__init__(name) |
| # super().__init__(name) |
| |
| self.subject = subject |
| subject.register(self) |
| |
| # 觀察者發出更新通知,不用單獨告訴訂閱者,由訂閱者自行監聽 |
| def update(self, content): |
| print(self.__class__.__name__ + '::update() [subject.name = ' + |
| self.subject.name + ' content = ' + content + ']') |
| # ConcreteObserver2.py 具體的觀察者實現類,也可以看成訂閱者,關聯對應的主題類。 |
| # 不同的觀察者可以對應不同的主題。 |
| from src.ObserverAPI import ObserverAPI |
| |
| |
| # 具體的觀察者實現類,也可以看成訂閱者,關聯對應的主題類。 |
| # 不同的觀察者可以對應不同的主題。 |
| class ConcreteObserver2(ObserverAPI): |
| # 這里沒有在構造器就綁定某個主題,而是從客戶角度去注冊觀察者 |
| # 觀察者發出更新通知,觀察者自行監聽 |
| |
| # def update(self, content): |
| # print(self.__class__.__name__ + '::update() [content = ' + content +']') |
| |
| pass |
抽象主題類
| # Subject.py 定義抽象主題類或者接口,供具體主題類繼承 |
| class Subject: |
| |
| def __init__(self, name): |
| self.name = name |
| self.observers = [] |
| |
| def get_name(self): |
| return self.name |
| |
| def set_name(self, name): |
| self.name = name |
| |
| def register(self, observer): |
| print(self.__class__.__name__ + '::register() [observer = ' + |
| observer.__class__.__name__ + ']') |
| |
| self.observers.append(observer) |
| |
| def remove(self, observer): |
| self.observers.remove(observer) |
| |
| # 通知由具體類來實現邏輯 |
| def notify(self, name): |
| pass |
具體主題類
| // ConcreteSubject.py 觀察者主題類,也是發布者,重寫具體的通知方法。不同主題可以關聯不同的觀察者。 |
| from src.Subject import Subject |
| |
| |
| # 觀察者主題類,也是發布者,重寫具體的通知方法。不同主題可以關聯不同的觀察者。 |
| class ConcreteSubject(Subject): |
| # 不同的主題類有自己的通知方法,批量通知綁定的觀察者 |
| def notify(self, content): |
| print(self.__class__.__name__ + '::notify() [content = ' + content + |
| ']') |
| for observer in self.observers: |
| observer.update(content) |
測試調用
| import sys |
| import os |
| |
| os_path = os.getcwd() |
| sys.path.append(os_path) |
| |
| from src.ConcreteSubject import ConcreteSubject |
| from src.ConcreteObserver import ConcreteObserver |
| from src.ConcreteObserver2 import ConcreteObserver2 |
| |
| |
| def test(): |
| ''' |
| * 觀察者模式應用非常廣泛,主要是觀察者提前綁定到發布者 |
| * 當發布者發布消息時,批量廣播通知,而無需逐一通知 |
| * 觀察者監聽到消息后自己決定采取哪一種行為 |
| ''' |
| |
| # 定義一個主題,也就是發布者 |
| concrete_subject = ConcreteSubject('subject1') |
| # 再聲明觀察者,通過構造器注冊到主題上 |
| observer1 = ConcreteObserver(concrete_subject, 'observer1') |
| # 也可以單獨給主題注冊一個新的觀察者 |
| observer2 = ConcreteObserver2('observer2') |
| concrete_subject.register(observer2) |
| # 可以移除觀察者對象 |
| # concrete_subject.remove(observer1) |
| |
| # 主題開始發布新通知,各觀察者自動更新 |
| concrete_subject.notify('hello, this is broadcast.') |
| |
| |
| if __name__ == '__main__': |
| print(__file__) |
| print("test start:") |
| test() |
| |
更多語言版本
不同語言實現設計模式:https://github.com/microwind/design-pattern
??