觀察者模式
- 是一種行爲型設計模式
行爲型設計模式
- 創建型模式基於對象的創建機制,隔離對象的創建細節,使得代碼能夠與對象類***相互獨立***。
- 結構型設計模式用於***設計對象和類的結構***,從而優化他們的結構,和他們之間的關係。
- 行爲型設計模式在於***關注對象的職責***,用來處理對象之間的交互。
實現
- 定義對象之間一對多的關係,使得對象的變化可以通知其他對象
- 封裝主題核心組件
- 實例:分佈式系統中事件服務、新聞機構、股票通知
一個新聞發佈機構和他的訂閱者們(觀察者們)例子:
# 主題行爲類
class NewsPublisher:
def __init__(self):
self.__subscribers = [] # 存放這個類的對象的觀察者們
self.__latestNews = None
# 觀察者通過這個方法註冊進來,放在客戶端的列表裏
def attach(self, subscriber):
self.__subscribers.append(subscriber)
# 刪除觀察者
def detach(self):
return self.__subscribers.pop()
# 返回已經註冊的所有觀察者
def subscribers(self):
return [type(x).__name__ for x in self.__subscribers]
# 遍歷所有觀察者,通過觀察者的update方法打印出觀察者獲取的最新消息
def notify_subscribers(self):
for sub in self.__subscribers:
sub.update()
# 添加新消息
def add_news(self, news):
self.__latestNews = news
# 返回最新消息
def get_news(self):
return self.__latestNews
# 觀察者接口,抽象方法
from abc import ABCMeta, abstractmethod
class Subscriber(metaclass=ABCMeta):
@abstractmethod
def update(self):
pass
# 觀察者email
class EmailSubscriber:
def __init__(self, publisher):
self.publisher = publisher
self.publisher.attach(self)
def update(self):
print(type(self).__name__, self.publisher.get_news())
# 觀察者sms
class SMSSubscriber:
def __init__(self, publisher):
self.publisher = publisher
self.publisher.attach(self)
def update(self):
print(type(self).__name__, self.publisher.get_news())
# 用來演示Observers和Subject的鬆耦合關係
class AnyOtherSubscriber:
def __init__(self, publisher):
self.publisher = publisher
self.publisher.attach(self)
def update(self):
print(type(self).__name__, self.publisher.get_news())
if __name__ == '__main__':
# 創建一個客戶端對象
news_publisher = NewsPublisher()
# 創建3個觀察者
for subscribers in [SMSSubscriber, EmailSubscriber, AnyOtherSubscriber]:
subscribers(news_publisher)
# 打印出所有觀察者列表
print('\nsubscribers:', news_publisher.subscribers())
# 添加一個消息
news_publisher.add_news('hello')
# 提醒所有觀察者新消息,
news_publisher.notify_subscribers()
print(type(news_publisher.detach()).__name__)
print(news_publisher.subscribers())
news_publisher.add_news('第二個消息')
news_publisher.notify_subscribers()
觀察者模式的通知方式
- 拉模型
- 觀察者是積極的,每當發生變化主題都會給所有觀察者廣播
- 出現變化的時候,觀察者負責獲取
- 效率比較低,因爲他要先主題通知觀察者,再觀察者從主題那提取數據
- 推模型
- 主題是主導的一方,只發送觀察者所需的數據
- 出現的變化,由主題直接推送到觀察者,避免了不必要的數據,節省了時間
觀察者模式優缺點
優點:
- 可以保持鬆耦合
- 無需對主題或者觀察者修改也能高效的發送數據到其他對象。
- 可以隨時添加刪除觀察者
缺點:
- 觀察者接口必須有具體觀察者實現,需要繼承。無法進行組合,因爲他可以實例化。
- 實現不當會增加觀察者的複雜性
- 通知可能不可靠,導致不一致性。
一些問題
- 可能存在多個主題或者觀察者嗎?
可以,這種情況要正常工作就必須通知觀察者那些主題發生了變化以及各個主題中發生了什麼變化 - 誰負責觸發更新?
一般情況下,主題觸發更新方法。如果有特別需要,可以觀察者觸發,但要注意頻率不能太高。 - 主題或者觀察者可以在任何其他用例中訪問嗎?
可以,這是鬆耦合的強大體現,可以獨立使用。