訂閱者模式,公眾號、B站、快手用了都說好!

大家好,今天和大家來聊一個新的設計模式——訂閱者模式

這個模式在我們的生活當中非常常見,可以說是幾乎所有的媒體平台都用或多或少地用到了這個模式。比如公眾號,我們來仔細梳理一下公眾號這個平台當中的整個邏輯,會發現其實這裡面一共有三方存在,這三方呈一個三角關係。

三方訂閱關係

畫出來的話大概是這個樣子:

這張圖大家應該很好理解,TechFlow每天把新的文章發布到公眾號平台上,平台會把內容推送給那些關注了TechFlow的用戶,大家正是因為關注了TechFlow才讀到了這篇文章。

所以在訂閱制的內容平台當中,其實是一個三方關係,而不是讀者和作者兩方關係,平台在當中起到了媒介的作用。只是很多時候作為讀者,我們可能會忽略平台的存在。

既然存在三方關係,我們在實現相關邏輯的時候就需要把這三方剝離出來,單獨實現各自的邏輯,這樣程式碼的耦合性才最低,可以更加方便以後的拓展。

程式碼實現

訂閱者模式其實沒有太多內容需要講,我們只需要記住一點,就是盡量讓屬於不同實體的程式碼邏輯分開,而不是耦合在一起就可以了,沒有太多其他的門道。

我們先來看這個模式當中最簡單的部分,也就是Publisher(作者)這個部分。對於作者而言,它是最簡單的,因為它能做的事情非常少,就只有發布內容而已。當然一般現在也會有一些作者保護機制,比如說可以拉黑一些不喜歡的用戶什麼的,但這裡我們不需要考慮那麼多,只需要考慮基本功能就行了。對於作者而言,最基本的功能就是發布內容。

程式碼非常簡單,就只有幾行。

class Publisher:

    def __init__(self, msg_center):
        self.provider = msg_center

    def publish(self, msg):
        self.provider.notify(msg)

因為作者自身是沒辦法發布內容的,作者是把要發布的內容上傳到平台,平台代替作者去發布的。體現在這個類當中就是我們調用了provider也就是平台去執行通知(notify)的操作,把新發布的內容推送到讀者端。

讀者端稍微複雜一點,因為讀者不僅可以訂閱,還可以取關,並且收到了消息之後還可以打開以及一些互動。這裡我們把功能簡化,就留下了三個最基本的功能,分別是關注、取關以及操作。

class Subscriber:
    
    def __init__(self, name, msg_center):
        self.name = name
        self.provider = msg_center

    def subscribe(self, msg):
        self.provider.subscribe(msg, self)

    def unsubscribe(self, msg):
        self.provider.unsubscribe(msg, self)

    def run(self, msg):
        print('{} got {}'.format(self.name, msg))

從程式碼當中我們可以看到,讀者端的操作其實也是和平台交互。平台是讀者和作者之間的媒介,讀者和作者之間不直接發生關聯。這其實是非常不錯的設計,如果關聯和依賴很多,就會出現要開發新功能的時候畏手畏腳,會影響其他模組的情況發生。

最後,我們來看平台的部分,平台的部分其實也不複雜,只是用一個dict存儲了讀者和作者之間的訂閱關係而已。其實這裡沒必要使用setdefault,使用defaultdict會更好。

class Provider:

    def __init__(self):
        self.msg_queue = []
        self.subscribers = {}

    def notify(self, msg):
        self.msg_queue.append(msg)

    def subscribe(self, msg, subscriber):
        self.subscribers.setdefault(msg, []).append(subscriber)

    def unsubscribe(self, msg, subscriber):
        self.subscribers[msg].remove(subscriber)

    def update(self):
        # 遍歷所有的作者
        for msg in self.msg_queue:
            # 遍歷所有訂閱了msg的讀者進行推送
            for sub in self.subscribers.get(msg, []):
                sub.run(msg)
        self.msg_queue = []

這裡我把執行的測試程式碼也放上來,大家感興趣可以自己試驗一下。

if __name__ == '__main__':
    message_center = Provider()
    fftv = Publisher(message_center)

    jim = Subscriber('jim', message_center)
    jim.subscribe('cartoon')

    jack = Subscriber('jack', message_center)
    jack.subscribe('music')

    gee = Subscriber('gee', message_center)
    gee.subscribe('movie')

    vani = Subscriber('vani', message_center)
    vani.subscribe('movie')

    fftv.publish('cartoon')
    fftv.publish('music')
    fftv.publish('ads')
    fftv.publish('cartoon')
    fftv.publish('cartoon')
    fftv.publish('movie')
    fftv.publish('blank')

    message_center.update()

從程式碼層面來說,這個設計模式沒有太多的難度,主要是一種解耦的思想。這一個思想很重要,在實際開發當中,我們決不能僅僅滿足於實現產品經理的功能,因為產品經理往往是不懂這些系統之間架構和軟體工程的。我們之所以需要設計模式只有30%的原因是為了更好的效率以及更簡潔的程式碼,剩下70%的原因其實都是為了抵禦日後功能需求的變化。

廢話不多說了,今天的文章就到這裡,衷心祝願大家每天都有所收穫。如果還喜歡今天的內容的話,請來一個三連支援吧~(點贊、關注、轉發