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

observer-pattern.png
Java代碼
觀察者接口
// ObserverAPI.java 觀察者接口,Java 9已經(jīng)默認(rèn)支持Observer接口
// 這里避免沖突采取ObserverAPI命名
public interface ObserverAPI {
public Subject subject = null;
public void update(String content);
}
具體觀察者
// ConcreteObserver.java 具體的觀察者實(shí)現(xiàn)類,也可以看成訂閱者,關(guān)聯(lián)對(duì)應(yīng)的主題類。
// 不同的觀察者也可以對(duì)應(yīng)多個(gè)主題
public class ConcreteObserver implements ObserverAPI {
public Subject subject;
// 給觀察者綁定主題,同時(shí)把觀察者添加到主題列表
public ConcreteObserver(Subject subject) {
this.subject = subject;
this.subject.register((ObserverAPI) this);
}
// 觀察者發(fā)出更新通知,不用單獨(dú)告訴訂閱者,由訂閱者自行監(jiān)聽(tīng)
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 具體的觀察者實(shí)現(xiàn)類,也可以看成訂閱者,關(guān)聯(lián)對(duì)應(yīng)的主題類。
// 不同的觀察者可以對(duì)應(yīng)不同的主題。
public class ConcreteObserver2 implements ObserverAPI {
// 這里沒(méi)有在構(gòu)造器就綁定某個(gè)主題,而是從客戶角度去注冊(cè)觀察者
public ConcreteObserver2() {
}
// 觀察者發(fā)出更新通知,觀察者自行監(jiān)聽(tīng)
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);
}
// 通知由具體類來(lái)實(shí)現(xiàn)邏輯
public abstract void notify(String name);
}
具體主題類
// ConcreteSubject.java 觀察者主題類,也是發(fā)布者,重寫具體的通知方法。不同主題可以關(guān)聯(lián)不同的觀察者。
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);
}
}
}
測(cè)試調(diào)用
/**
* 觀察者模式應(yīng)用非常廣泛,主要是觀察者提前綁定到發(fā)布者
* 當(dāng)發(fā)布者發(fā)布消息時(shí),批量廣播通知,而無(wú)需逐一通知
* 觀察者監(jiān)聽(tīng)到消息后自己決定采取哪一種行為
*/
// 定義一個(gè)主題,也就是發(fā)布者
Subject concreteSubject = new ConcreteSubject("subject1");
// 再聲明觀察者,通過(guò)構(gòu)造器注冊(cè)到主題上
ObserverAPI observer1 = new ConcreteObserver(concreteSubject);
// 也可以單獨(dú)給主題注冊(cè)一個(gè)新的觀察者
concreteSubject.register(new ConcreteObserver2());
// 可以移除觀察者對(duì)象,可以打開(kāi)注釋試下
// concreteSubject.remove(observer1);
// 主題開(kāi)始發(fā)布新通知,各觀察者自動(dòng)更新
concreteSubject.notify("hello, this is broadcast.");
Python代碼
觀察者接口
# ObserverAPI.py 觀察者抽象父類,定義一些公共方法
class ObserverAPI:
def __init__(self, name):
self.name = name
# 觀察者發(fā)出更新通知,觀察者自行監(jiān)聽(tīng)
def update(self, content):
print(self.__class__.__name__ + '::update() [content = ' + content + ']')
def set_name(self, name):
self.name = name
具體觀察者
# ConcreteObserver.py 具體的觀察者實(shí)現(xiàn)類,也可以看成訂閱者,關(guān)聯(lián)對(duì)應(yīng)的主題類。
# 不同的觀察者也可以對(duì)應(yīng)多個(gè)主題
from src.ObserverAPI import ObserverAPI
# 具體的觀察者實(shí)現(xiàn)類,也可以看成訂閱者,關(guān)聯(lián)對(duì)應(yīng)的主題類。
# 不同的觀察者也可以對(duì)應(yīng)多個(gè)主題
class ConcreteObserver(ObserverAPI):
# 給觀察者綁定主題,同時(shí)把觀察者添加到主題列表
def __init__(self, subject, name):
ObserverAPI.__init__(self, name)
# python3支持的父類調(diào)用
# super(ConcreteObserver, self).__init__(name)
# super().__init__(name)
self.subject = subject
subject.register(self)
# 觀察者發(fā)出更新通知,不用單獨(dú)告訴訂閱者,由訂閱者自行監(jiān)聽(tīng)
def update(self, content):
print(self.__class__.__name__ + '::update() [subject.name = ' +
self.subject.name + ' content = ' + content + ']')
# ConcreteObserver2.py 具體的觀察者實(shí)現(xiàn)類,也可以看成訂閱者,關(guān)聯(lián)對(duì)應(yīng)的主題類。
# 不同的觀察者可以對(duì)應(yīng)不同的主題。
from src.ObserverAPI import ObserverAPI
# 具體的觀察者實(shí)現(xiàn)類,也可以看成訂閱者,關(guān)聯(lián)對(duì)應(yīng)的主題類。
# 不同的觀察者可以對(duì)應(yīng)不同的主題。
class ConcreteObserver2(ObserverAPI):
# 這里沒(méi)有在構(gòu)造器就綁定某個(gè)主題,而是從客戶角度去注冊(cè)觀察者
# 觀察者發(fā)出更新通知,觀察者自行監(jiān)聽(tīng)
# 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)
# 通知由具體類來(lái)實(shí)現(xiàn)邏輯
def notify(self, name):
pass
具體主題類
// ConcreteSubject.py 觀察者主題類,也是發(fā)布者,重寫具體的通知方法。不同主題可以關(guān)聯(lián)不同的觀察者。
from src.Subject import Subject
# 觀察者主題類,也是發(fā)布者,重寫具體的通知方法。不同主題可以關(guān)聯(lián)不同的觀察者。
class ConcreteSubject(Subject):
# 不同的主題類有自己的通知方法,批量通知綁定的觀察者
def notify(self, content):
print(self.__class__.__name__ + '::notify() [content = ' + content +
']')
for observer in self.observers:
observer.update(content)
測(cè)試調(diào)用
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():
'''
* 觀察者模式應(yīng)用非常廣泛,主要是觀察者提前綁定到發(fā)布者
* 當(dāng)發(fā)布者發(fā)布消息時(shí),批量廣播通知,而無(wú)需逐一通知
* 觀察者監(jiān)聽(tīng)到消息后自己決定采取哪一種行為
'''
# 定義一個(gè)主題,也就是發(fā)布者
concrete_subject = ConcreteSubject('subject1')
# 再聲明觀察者,通過(guò)構(gòu)造器注冊(cè)到主題上
observer1 = ConcreteObserver(concrete_subject, 'observer1')
# 也可以單獨(dú)給主題注冊(cè)一個(gè)新的觀察者
observer2 = ConcreteObserver2('observer2')
concrete_subject.register(observer2)
# 可以移除觀察者對(duì)象
# concrete_subject.remove(observer1)
# 主題開(kāi)始發(fā)布新通知,各觀察者自動(dòng)更新
concrete_subject.notify('hello, this is broadcast.')
if __name__ == '__main__':
print(__file__)
print("test start:")
test()
更多語(yǔ)言版本
不同語(yǔ)言實(shí)現(xiàn)設(shè)計(jì)模式:https://github.com/microwind/design-pattern