python設計模式之觀察者模式

  說到觀察者模式,在我腦海中總是閃現,這傢伙跟消息隊列的主題發布訂閱有什麼關係,雖然本人對消息隊列沒有很深的研究,但是憑直覺我就認為消息隊列的實現就使用了觀察者模式吧,所以本文就來模擬消息隊列的丐版實現闡述觀察者模式是怎樣玩的。

觀察者模式的GOF官方解釋是: 定義對象間的一種一對多(變化)的依賴關係, 以便當一個對象(Subject)的狀態發生改變時,所有依賴於它的對象都得到通知並更新。

  觀察者模式類圖如下:

 

  主要構成就是主題基類, 觀察者基類及其他們的實現。接下來我們開始設計屬於我們自己的消息隊列。

 

01、 首先設計主題基類

 

from abc import ABC


class Subject(ABC):

    def __init__(self):
        self.observers = list()

    def add_observer(self, observer):
        self.observers.append(observer)

    def pop_observer(self, observer):
        self.observers.remove(observer)

    def notify(self):
        for observer in self.observers:
            observer.update()

 

 

在Subject基類中,我們需要定義一個觀察者列表用於盛放觀察者對象,然後我們需要有添加和刪除觀察者的方法,最後一個必要的方法就是通知觀察者更新。

 

02、設計我們的主題子類

class GameSubject(Subject):

    def notify(self, msg):
        for observer in self.observers:
            observer.update(msg)

 

  我們設計一個遊戲主題類,專門給觀察者推送遊戲消息,所以我們重寫了nitify方法。

 

03、設計我們的觀察者子類

from queue import Queue


class Observer:
    def __init__(self):
        self.queue = Queue(100)
        
    def update(self):
        pass

 

 

  在觀察者基類中,我們定一個隊列用於接收主題發布的消息, 還有聲明一個更新方法,用於給子類繼承。

 

04、設計我們的觀察者子類

class LolObserver(Observer):
    
    def __init__(self, name):
        self.name = name
        super().__init__()
        
    def update(self, msg):
        self.queue.put(msg)
        
    def get_msg(self):
        while not self.queue.empty():
            msg = self.queue.get()
            print(self.name + "正在讀取消息:" + msg)


class DNFObserver(Observer):

    def __init__(self, name):
        self.name = name
        super().__init__()

    def update(self, msg):
        self.queue.put(msg)

    def get_msg(self):
        while not self.queue.empty():
            msg = self.queue.get()
            print(self.name + "正在讀取消息:" + msg)

 

 

  在我們的觀察者子類中,我們主要定義了,對隊列的IO操作。

 

05、 主程式

if __name__ == "__main__":
    game_subject = GameSubject()
    lol_observer = LolObserver('lol選手')
    dnf_observer = DNFObserver("DNF選手")
    game_subject.add_observer(lol_observer)
    game_subject.add_observer(dnf_observer)
    game_subject.notify("第一屆遊戲大賽正在開始")
    game_subject.notify("我們友情兩大遊戲金牌選手")
    game_subject.notify("誰會是第一名呢?")
    game_subject.pop_observer(dnf_observer)
    game_subject.notify("會是LOL選手嗎?")
    game_subject.notify("貌似DNF選手掉線了啊")
    game_subject.add_observer(dnf_observer)
    game_subject.notify("我們的選手又回來了")
    lol_observer.get_msg()
    print("="*77)
    dnf_observer.get_msg()

 

運行結果如下:

/usr/local/bin/python3.7 /Users/bytedance/PycharmProjects/untitled3/模版設計模式/觀察者模式.py
lol選手正在讀取消息:第一屆遊戲大賽正在開始
lol選手正在讀取消息:我們友情兩大遊戲金牌選手
lol選手正在讀取消息:誰會是第一名呢?
lol選手正在讀取消息:會是LOL選手嗎?
lol選手正在讀取消息:貌似DNF選手掉線了啊
lol選手正在讀取消息:我們的選手又回來了
=============================================================================
DNF選手正在讀取消息:第一屆遊戲大賽正在開始
DNF選手正在讀取消息:我們友情兩大遊戲金牌選手
DNF選手正在讀取消息:誰會是第一名呢?
DNF選手正在讀取消息:我們的選手又回來了

Process finished with exit code 0

 

  到此我們的丐版的消息隊列就完成了,哈哈,以此類推諸如微信群功能,都可以用此模式去實現,到此感覺觀察者模式不像是一種模式,更像是一種業務的實現的技巧,但是它的關鍵點在於,你在遍歷觀察者列表處的巧妙,利用面向對象多態的特性,只要你繼承自觀察者基類,都可以調用update方法,無須是具體的觀察者。

 

07、總結

  1. 觀察者設計模式使得我們可以獨立地改變主題和觀察者,從而使二者的依賴關係達到松耦合的目的。
  2. 目標發送通知時,我們無須指定接收者,消息自動傳播到接受者處。
  3. 但是當觀察者過多時可能會產生性能問題,因為我們是在遍歷觀察者列表

 

最後還是奉上我們的設計模式八大設計原則:

  1. 依賴倒置原則(DIP)
  • 高層模組(穩定)不應該依賴於低層模組(變化),二者都應該依賴於抽象(穩定) 。
  • 抽象(穩定)不應該依賴於實現細節(變化) ,實現細節應該依賴於抽象(穩定)。
  1. 開放封閉原則(OCP)
  • 對擴展開放,對更改封閉。
  • 類模組應該是可擴展的,但是不可修改。
  1. 單一職責原則(SRP)
  • 一個類應該僅有一個引起它變化的原因。
  • 變化的方向隱含著類的責任。
  1. Liskov 替換原則(LSP)
  • 子類必須能夠替換它們的基類(IS-A)。
  • 繼承表達類型抽象。
  1. 介面隔離原則(ISP)
  • 不應該強迫客戶程式依賴它們不用的方法。
  • 介面應該小而完備。
  1. 優先使用對象組合,而不是類繼承
  • 類繼承通常為「白箱復用」,對象組合通常為「黑箱復用」 。
  • 繼承在某種程度上破壞了封裝性,子類父類耦合度高。
  • 而對象組合則只要求被組合的對象具有良好定義的介面,耦合度低。
  1. 封裝變化點
  • 使用封裝來創建對象之間的分界層,讓設計者可以在分界層的一側進行修改,而不會對另一側產生不良的影響,從而實現層次間的松耦合。
  1. 針對介面編程,而不是針對實現編程
  • 不將變數類型聲明為某個特定的具體類,而是聲明為某個介面。
  • 客戶程式無需獲知對象的具體類型,只需要知道對象所具有的介面。
  • 減少系統中各部分的依賴關係,從而實現「高內聚、松耦合」的類型設計方案