大話設計模式:第14章 觀察者模式

第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發出通知時並不需要知道誰是它的觀察者。而任何一個具體觀察者不知道也不需要知道其他觀察者的存在

觀察者模式的使用

  1. 當一個對象的改變需要同時改變其他對象的時候時,而且它不知道具體有多少對象有待改變時,應該考慮使用觀察者模式

  2. 當一個抽象模型有兩個方面,其中一方面依賴於另一方面。這時用觀察者模式可以將這兩者封裝在獨立的對象中使它們各自獨立地改變和複用。

觀察者模式所做的工作是在解除耦合,讓耦合雙方都依賴於抽象,而不是依賴於具體,從而使得各自的變化都不會影響另一邊的變化。

觀察者模式示例

任務:前臺監督老闆是否已經到來,並通知同事

在這裏插入圖片描述

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直播,繼續工作!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章