第14章:觀察者模式
觀察者模式
觀察者(observer)模式:定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態發生變化時,會通知所有觀察者對象,使它們能夠自動更新自已。
Subject
類:主題或抽象通知者,一般用一個抽象類或者一個接口實現。它把所有對觀察者對象的引用保存在一個聚集裏,每個主題都可以有任何數量的觀察者。抽象主題提供一個接口,可以增加和刪除觀察者對象。
Observer
類:抽象觀察者,爲所有的具體觀察者定義一個接口,在得到主題的通知時更新自己。這個接口叫做更新接口。抽象觀察者一般用一個抽象類或者一個接口實現。更新接口通常包含一個更新方法Update()
方法。
ConcreteSubject
類:具體主題或具體通知者,將有關狀態存入具體觀察者對象;在具體主題的內部狀態改變時,給所有登記過的觀察者發出通知。具體主題角色通常用一個具體子類實現。
ConcreteObserver
類:具體觀察者,實現抽象觀察者角色所要求的更新接口,以便使本身的狀態與主題的狀態相協調。具體觀察者角色可以保存一個指向具體主題對象的引用。具體觀察者角色通常用一個具體子類實現。
客戶端代碼
結果顯示
from abc import ABC, abstractmethod
from typing import Text
class Observer(ABC):
@abstractmethod
def update(self):
pass
class Subject(ABC):
def __init__(self) -> None:
self.__observers = []
def attach(self, observer: Observer) -> None:
"""增加觀察者"""
self.__observers.append(observer)
def detach(self, observer: Observer) -> None:
"""移除觀察者"""
self.__observers.remove(observer)
def notify(self) -> None:
"""通知"""
for observer in self.__observers:
observer.update()
class ConcreteSubject(Subject):
def __init__(self) -> None:
super(ConcreteSubject, self).__init__()
self.__subject_state = None
@property
def subject_state(self) -> Text:
return self.__subject_state
@subject_state.setter
def subject_state(self, value: Text) -> None:
self.__subject_state = value
class ConcreteObserver(Observer):
def __init__(self, subject: ConcreteSubject, name: Text) -> None:
self.__name = name
self.__observer_state = None
self.__subject = subject
def update(self) -> None:
self.__observer_state = self.__subject.subject_state
print("觀察者{}的新狀態是{}".format(self.__name, self.__observer_state))
@property
def subject(self) -> ConcreteSubject:
return self.__subject
@subject.setter
def subject(self, value: ConcreteSubject) -> None:
self.__subject = value
if __name__ == "__main__":
s = ConcreteSubject()
s.attach(ConcreteObserver(subject=s, name="X"))
s.attach(ConcreteObserver(subject=s, name="Y"))
s.attach(ConcreteObserver(subject=s, name="Z"))
s.subject_state = "ABC"
s.notify()
觀察者X的新狀態是ABC
觀察者Y的新狀態是ABC
觀察者Z的新狀態是ABC
觀察者模式特點
觀察者模式的動機:將一個系統分割成一系列相互協作的類有一個很不好的副作用,那就是需要維護相關對象間的一致性門我們不希望爲了維持一致性而使各類緊密鍋合甲這樣會給維護、擴展和重用都帶來不便。
觀察者模式的關鍵對象是主題Subject
和觀察者Observer
,一個Subject
可以有任意目的依賴於它的Observer
,一旦Subject
的狀態發生了改變,所有的Observer
都可以得到通知。Subject
發出通知時並不需要知道誰是它的觀察者。而任何一個具體觀察者不知道也不需要知道其他觀察者的存在
觀察者模式的使用:
-
當一個對象的改變需要同時改變其他對象的時候時,而且它不知道具體有多少對象有待改變時,應該考慮使用觀察者模式
-
當一個抽象模型有兩個方面,其中一方面依賴於另一方面。這時用觀察者模式可以將這兩者封裝在獨立的對象中使它們各自獨立地改變和複用。
觀察者模式所做的工作是在解除耦合,讓耦合雙方都依賴於抽象,而不是依賴於具體,從而使得各自的變化都不會影響另一邊的變化。
觀察者模式示例
任務:前臺監督老闆是否已經到來,並通知同事
from abc import ABC, abstractmethod
from typing import Text
class Observer(ABC):
def __init__(self, name: Text, sub: object) -> None:
self._name = name
self._sub = sub
@abstractmethod
def update(self) -> None:
pass
class Subject(ABC):
def __init__(self) -> None:
self._subject_state = None
@abstractmethod
def attach(self, observer: Observer) -> None:
pass
@abstractmethod
def detach(self, observer: Observer) -> None:
pass
@abstractmethod
def notify(self) -> None:
pass
@property
def subject_state(self) -> Text:
return self._subject_state
@subject_state.setter
def subject_state(self, value: Text) -> None:
self._subject_state = value
class Boss(Subject):
def __init__(self):
# 同事列表
self.__observers = []
self.__action = ""
super(Boss, self).__init__()
def attach(self, observer: Observer) -> None:
"""
增加
"""
self.__observers.append(observer)
def detach(self, observer: Observer) -> None:
"""
減少
"""
self.__observers.remove(observer)
def notify(self) -> None:
"""
通知
"""
for o in self.__observers:
o.update()
@property
def action(self) -> Text:
return self.__action
@action.setter
def action(self, value: Text) -> None:
self.__action = value
class Secretary(Subject):
def __init__(self):
# 同事列表
self.__observers = []
self.__action = ""
super(Secretary, self).__init__()
def attach(self, observer: Observer) -> None:
"""
增加
"""
self.__observers.append(observer)
def detach(self, observer: Observer) -> None:
"""
減少
"""
self.__observers.remove(observer)
def notify(self) -> None:
"""
通知
"""
for o in self.__observers:
o.update()
@property
def action(self) -> Text:
return self.__action
@action.setter
def action(self, value: Text) -> None:
self.__action = value
class StockObserver(Observer):
def __init__(self, name: Text, sub: Subject) -> None:
super(StockObserver, self).__init__(name, sub)
def update(self) -> None:
print(
"{}{}關閉股票行情,繼續工作!".format(
self._sub.subject_state, self._name
)
)
class NBAObserver(Observer):
def __init__(self, name: Text, sub: Subject) -> None:
super(NBAObserver, self).__init__(name, sub)
def update(self) -> None:
print(
"{}{}關閉NBA直播,繼續工作!".format(
self._sub.subject_state, self._name
)
)
if __name__ == "__main__":
# 前臺小姐童子喆
tongzizhe = Secretary()
# 看股票的同事
tongshi1 = StockObserver("魏關奼", tongzizhe)
# 看NBA的同事
tongshi2 = NBAObserver("易管查", tongzizhe)
# 前臺記下兩位同事
tongzizhe.attach(tongshi1)
tongzizhe.attach(tongshi2)
# 發現老闆回來了
tongzizhe.subject_state = "老闆回來了!"
# 發出通知
tongzizhe.notify()
# 前臺未能發出通知
# 老闆胡漢三
huhansan = Boss()
# 看股票的同事
tongshi1 = StockObserver("魏關奼", huhansan)
# 看NBA的同事
tongshi2 = NBAObserver("易管查", huhansan)
huhansan.attach(tongshi1)
huhansan.attach(tongshi2)
# 魏關奼未注意到老闆回來
huhansan.detach(tongshi1)
# 老闆回來了
huhansan.subject_state = "我胡漢三又回來了!"
# 發出通知
huhansan.notify()
老闆回來了!魏關奼關閉股票行情,繼續工作!
老闆回來了!易管查關閉NBA直播,繼續工作!
我胡漢三又回來了!易管查關閉NBA直播,繼續工作!
觀察者模式的不足
儘管使用了依賴倒轉原則,但是“抽象通知者”還是依賴“抽象觀察者”,即萬一沒有抽象觀察者這樣的接口,通知的功能無法完成,此外,具體觀察者不一定要調用“更新”方法。
事件委託
委託是一種引用方法的類型。一旦爲委託分配了方法,委託將與該方法具有完全相同的行爲。委託方法的使用可以像其他任何方法一樣,具有參數和返回值。委託可以看作是對函數的抽象,是函數的‘類’,委託的實例將代表一個具體的函數。
一個委託可以搭載多個方法,所有方法被依次喚起。委託對象所搭載的方法並不需要屬於同一個類。
委託的前提:委託對象所搭載的所有方法必須具有相同的原形和形式,即擁有相同的參數列表和返回值類型。
事件委託示例
from abc import ABC, abstractmethod
from typing import Text
聲明一個委託,名稱叫EventHandler
(事件處理程序),無參數,無返回值。
from typing import Callable
class EventHandler(object):
def __init__(self) -> None:
self.__events = []
def __call__(self) -> None:
for event in self.__events:
event()
def add(self, handler: Callable):
self.__events.append(handler)
class Subject(ABC):
@abstractmethod
def notify(self) -> None:
pass
@property
@abstractmethod
def subject_state(self) -> Text:
pass
@subject_state.setter
@abstractmethod
def subject_state(self, value: Text) -> None:
pass
class Boss(Subject):
def __init__(self) -> None:
self.__action = None
self.update = EventHandler()
def notify(self) -> None:
self.update()
@property
def subject_state(self) -> Text:
return self.__action
@subject_state.setter
def subject_state(self, value: Text):
self.__action = value
class Secretary(Subject):
def __init__(self) -> None:
self.__action = None
self.update = EventHandler()
def notify(self) -> None:
self.update()
@property
def subject_state(self) -> Text:
return self.__action
@subject_state.setter
def subject_state(self, value: Text):
self.__action = value
class StockObserver(object):
def __init__(self, name: Text, sub: Subject) -> None:
self.__name = name
self.__sub = sub
def close_stock_market(self) -> None:
print(
"{}{}關閉股票行情,繼續工作!".format(
self.__sub.subject_state, self.__name
)
)
class NBAObserver(object):
def __init__(self, name: Text, sub: Subject) -> None:
self.__name = name
self.__sub = sub
def close_nba_direct_seeding(self) -> None:
print(
"{}{}關閉NBA直播,繼續工作!".format(
self.__sub.subject_state, self.__name
)
)
if __name__ == "__main__":
# 前臺小姐童子喆
tongzizhe = Secretary()
# 看股票的同事
tongshi1 = StockObserver("魏關奼", tongzizhe)
# 看NBA的同事
tongshi2 = NBAObserver("易管查", tongzizhe)
# 前臺記下兩位同事
tongzizhe.update.add(tongshi1.close_stock_market)
tongzizhe.update.add(tongshi2.close_nba_direct_seeding)
# 發現老闆回來了
tongzizhe.subject_state = "老闆回來了!"
# 發出通知
tongzizhe.notify()
# 前臺未能發出通知
# 老闆胡漢三
huhansan = Boss()
# 看股票的同事
tongshi1 = StockObserver("魏關奼", huhansan)
# 看NBA的同事
tongshi2 = NBAObserver("易管查", huhansan)
# 將“看股票者”的“關閉股票程序”方法和
# “看NBA者”的“關閉NBA直播”方法掛鉤到“老闆”的“更新”上,
# 也就是將兩不同類的不同方法委託給“老闆”類的“更新”了
huhansan.update.add(tongshi1.close_stock_market)
huhansan.update.add(tongshi2.close_nba_direct_seeding)
# 老闆回來了
huhansan.subject_state = "我胡漢三又回來了!"
# 發出通知
huhansan.notify()
老闆回來了!魏關奼關閉股票行情,繼續工作!
老闆回來了!易管查關閉NBA直播,繼續工作!
我胡漢三又回來了!魏關奼關閉股票行情,繼續工作!
我胡漢三又回來了!易管查關閉NBA直播,繼續工作!