[Python設計模式] 第14章 老闆來了——觀察者模式

github地址:https://github.com/cheesezh/python_design_patternspython

題目

用程序模擬如下情景,在一個辦公室裏,當老闆進門的時候,前臺祕書就偷偷通知辦公室裏的同事:「老闆來了」,辦公室裏的同事就會中止觀看股票,繼續工做。git

基礎版本

class Secretary():
    """
    通知者
    """
    def __init__(self):
        self.observers = []
        self.action = None
    
    def attach(self, observer):
        self.observers.append(observer)
        
    def notify(self):
        for observer in self.observers:
            observer.update()
            
class Observers():
    """
    觀察者
    """
    def __init__(self, name, informer):
        self.name = name
        self.informer = informer
        
    def update(self):
        print("{} {}關閉股票行情, 繼續工做.".format(self.informer.action, self.name))
        
def main():
    informer = Secretary()
    
    hh = Observers("賀賀", informer)
    mm = Observers("曼曼", informer)
    
    informer.attach(hh)
    informer.attach(mm)
    
    informer.action = "老闆來了!"
    
    informer.notify()
    
main()
老闆來了! 賀賀關閉股票行情, 繼續工做.
老闆來了! 曼曼關閉股票行情, 繼續工做.

點評

  • 上述代碼基本可以表示設定的場景,可是有一個問題,「通知者」和「觀察者」這兩個類互相耦合,即雙向耦合;
  • 假設另外一類「觀察者」不看股票,而是在看NBA比賽,那麼這類「觀察者」對應的update操做就不該該是「關閉股票行情」,而是「關閉NBA直播」;
  • 此外,除了前臺祕書能夠做爲「通知者」,老闆其實也是「通知者」,當老闆來了,各個「觀察者」也應該及時回到工做狀態;
  • 綜上,須要對「觀察者」和「通知者」進行進一步抽象

改進版本——雙向解耦

from abc import ABCMeta,abstractmethod


class Informer():
    """
    抽象通知者
    """
    __metaclass__ = ABCMeta
    
    @abstractmethod
    def attach(self, observer):
        pass
    
    @abstractmethod
    def detach(self, observer):
        pass
    
    @abstractmethod
    def notify(self):
        pass
    
class Boss(Informer):
    """
    具體通知者
    """
    def __init__(self):
        self.observers = []
        self.action = None
        # 老闆特有的初始化操做
        
    def attach(self, observer):
        print("聘用:{}".format(observer.name))
        self.observers.append(observer)
        
    def detach(self, observer):
        print("解聘:{}".format(observer.name))
        self.observers.remove(observer)
        
    def notify(self):
        print("--老闆發出強大的氣場--")
        for o in self.observers:
            o.update()
    

class Secretary(Informer):
    """
    具體通知者
    """
    def __init__(self):
        self.observers = []
        self.action = None
        # 祕書特有的初始化操做
        
    def attach(self, observer):
        print("關係和諧:{}".format(observer.name))
        self.observers.append(observer)
        
    def detach(self, observer):
        print("產生矛盾:{}".format(observer.name))
        self.observers.remove(observer)
        
    def notify(self):
        print("--祕書發送即時消息--")
        for o in self.observers:
            o.update() 


class Observer():
    """
    抽象觀察者
    """
    __mataclass__ = ABCMeta
    
    @abstractmethod
    def __init__(self, name, informer):  # 之因此要有informer,是爲了在觀察者內部訪問到informer的對象
        pass
    
    @abstractmethod
    def update(self):
        pass
    

class StockObserver(Observer):
    """
    具體觀察者
    """
    def __init__(self, name, informer):
        self.name = name
        self.informer = informer
        
    def update(self):
        print("{} {}關閉股票行情, 繼續工做.".format(self.informer.action, self.name))
        
        
class NBAObserver(Observer):
    """
    具體觀察者
    """
    def __init__(self, name, informer):
        self.name = name
        self.informer = informer
        
    def update(self):
        print("{} {}關閉NBA直播, 繼續工做.".format(self.informer.action, self.name))
        

def main():
    boss = Boss()
    
    hh = StockObserver("賀賀", boss)
    mm = NBAObserver("曼曼", boss)

    boss.attach(hh)
    boss.attach(mm)
    
    
    boss.action = "我回來了!"
    boss.notify()
    
    secretary = Secretary()
    
    mr = StockObserver("鳴人", secretary)
    zz = NBAObserver("佐助", secretary)
    xy = NBAObserver("小櫻", secretary)

    secretary.attach(mr)
    secretary.attach(zz)
    secretary.attach(xy)

    secretary.detach(xy)
    secretary.action = "老闆回來了!"
    secretary.notify()
    
main()
聘用:賀賀
聘用:曼曼
--老闆發出強大的氣場--
我回來了! 賀賀關閉股票行情, 繼續工做.
我回來了! 曼曼關閉NBA直播, 繼續工做.
關係和諧:鳴人
關係和諧:佐助
關係和諧:小櫻
產生矛盾:小櫻
--祕書發送即時消息--
老闆回來了! 鳴人關閉股票行情, 繼續工做.
老闆回來了! 佐助關閉NBA直播, 繼續工做.

觀察者模式(發佈-訂閱模式)

觀察者模式定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個主題對象。當這個主題對象在狀態發生變化時,會通知全部觀察者對象,使它們可以自動更新本身。github

Subject類,可翻譯爲主題或抽象通知者,通常用一個抽象類或者一個接口實現。它把全部對觀察者對象的引用保存在一個彙集裏,每一個主題均可以有任何數量的觀察者。抽象主題提供一個接口,能夠增長和刪除觀察者對象。app

Observer類,即抽象觀察者,爲全部的具體觀察者定義一個接口,在獲得主題的通知時更新本身。這個接口叫作更新接口。抽象觀察者通常用一個抽象類或者一個接口實現。更新接口一般包含一個update方法。翻譯

ConcreteSubject類,叫作具體主題或者具體通知者,將有關狀態存入具體觀察者對象。在具體主題的內部狀態改變時,給全部登記過的觀察者發出通知。code

ConcreteObserver類,即具體觀察者,實現抽象觀察者角色所要求的更新接口,以便使自己的狀態與主題的狀態相協調。若有須要,具體觀察者角色能夠保存一個指向具體主題對象的引用,以便獲取具體主題對象的狀態。orm

觀察者模式特色

講一個系統分割成一系列相互寫做的類有一個很很差的反作用,那就是須要維護相關對象間的一致性。咱們不但願爲了維持一致性而使各種緊密耦合,這樣會給維護,擴展和重用都帶來不便[DP]。而觀察者的關鍵對象使主題Subject和觀察者Observer,一個主題能夠有任意樹木的依賴它的Observer,一旦Subject狀態發生變化,全部的Observer均可以獲得通知。server

那麼何時使用觀察者模式呢?對象

當一個對象的改變須要同時改變其餘對象,並且它不知道具體有多少對象有待改變時,須要考慮使用觀察者模式。另外,當一個抽象模型有兩個方面,其中一個方面依賴另外一個方面,這時候使用觀察者模式能夠將這二者封裝在獨立的對象中使它們各自獨立地改變和複用。接口

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

相關文章
相關標籤/搜索