設(shè)計(jì)模式(Python)-觀(guān)察者模式

本系列文章是希望將軟件項(xiàng)目中最常見(jiàn)的設(shè)計(jì)模式用通俗易懂的語(yǔ)言來(lái)講解清楚振惰,并通過(guò)Python來(lái)實(shí)現(xiàn)歌溉,每個(gè)設(shè)計(jì)模式都是圍繞如下三個(gè)問(wèn)題:

  1. 為什么?即為什么要使用這個(gè)設(shè)計(jì)模式骑晶,在使用這個(gè)模式之前存在什么樣的問(wèn)題痛垛?
  2. 是什么?通過(guò)Python語(yǔ)言來(lái)去實(shí)現(xiàn)這個(gè)設(shè)計(jì)模式桶蛔,用于解決為什么中提到的問(wèn)題匙头。
  3. 怎么用?理解了為什么我們也就基本了解了什么情況下使用這個(gè)模式仔雷,不過(guò)在這里還是會(huì)細(xì)化使用場(chǎng)景蹂析,闡述模式的局限和優(yōu)缺點(diǎn)。

一些基本概念

在開(kāi)始講本次設(shè)計(jì)模式之前碟婆,我們先搞清楚軟件設(shè)計(jì)中的一些基本概念吧电抚。軟件設(shè)計(jì)中我們有一個(gè)常用的概念叫“耦合”,我們常常說(shuō)模塊之間要“解耦合”竖共,軟件設(shè)計(jì)要“松耦合”蝙叛。那么什么是“耦合”呢?
實(shí)際上耦合是指兩個(gè)模塊之間(模塊可以理解為兩坨代碼公给,所以可以是函數(shù)借帘,可以是類(lèi)蜘渣,或者更大范圍的系統(tǒng))發(fā)生了關(guān)系,比如一個(gè)模塊調(diào)用了另一個(gè)模塊的函數(shù)肺然,或者一個(gè)模塊需要獲取另一個(gè)模塊的數(shù)據(jù)蔫缸,總之有了聯(lián)系就有了耦合。所謂“解耦合”或者“松耦合”是說(shuō)讓兩個(gè)模塊之間的聯(lián)系沒(méi)有那么緊密狰挡,這樣一個(gè)模塊的變化不會(huì)影響另一個(gè)模塊的代碼捂龄。那么我們?nèi)绾蝸?lái)使我們的系統(tǒng)是一個(gè)“松耦合”的系統(tǒng)呢释涛?
實(shí)現(xiàn)“松耦合”最重要的就是抽象一致性加叁,白話(huà)就是要通過(guò)抽象的接口(Python中沒(méi)有接口的概念,可以理解為抽象的類(lèi))來(lái)聯(lián)系調(diào)用方和被調(diào)方唇撬。
一般的它匕,假設(shè)我們實(shí)現(xiàn)了兩個(gè)類(lèi)A和B,我們可能會(huì)如下調(diào)用

classcall.jpg

代碼可能是這樣的

class A(object):
    def __init__(self):
        self.obj = B(param1, param2)

    def method_of_a(self):
        self.obj.method_of_b(param3)


class B(object):
    def __init__(self, param1, param2):
        ...

    def method_of_b(self, param3):
         # do something
        ....

但是這種情況下窖认,如果B發(fā)生改變豫柬,或者想換成另一個(gè)類(lèi)C,這時(shí)就需要更改A的代碼扑浸,那么如果我們把A和B之間的聯(lián)系抽象出來(lái)烧给,通過(guò)接口(或者類(lèi)似接口的東西)來(lái)連接A和B我們就可以某種程度上屏蔽這種變化,如下圖所示:

interface.jpg

可能的代碼如下:

class A(object):
    def __init__(self, some_obj):
        self.obj = some_obj

    def method_of_a(self):
        self.obj.consistent_method(param3)

class B(object):
    def __init__(self, param1, param2):
        ...
    def consistent_method(self, param3):
        ...

class C(object):
    def __init__(self, param1, param2):
        ...
    def consistent_method(self, param3):
        ...

可以看出由于Python對(duì)參數(shù)沒(méi)有類(lèi)型約束喝噪,所以天然的支持接口础嫡,這也是Python靈活的地方之一。在這個(gè)例子中酝惧,我們的參數(shù)some_obj可以想象成一個(gè)抽象類(lèi)或者Java中的接口榴鼎,我們傳遞參數(shù)時(shí)可以傳遞具體實(shí)現(xiàn)類(lèi)的對(duì)象(比如B或者C的對(duì)象),但在這里只是一個(gè)抽象晚唇。

另外巫财,我們的B和C都實(shí)現(xiàn)了consistent_method,這就是一致性的體現(xiàn)哩陕。

為什么

假設(shè)我們?cè)O(shè)計(jì)了某種功能平项,當(dāng)用戶(hù)點(diǎn)擊頁(yè)面上的按鈕時(shí),我們需要為用戶(hù)做兩件事悍及,一是做頁(yè)面變換顯示出預(yù)期的效果葵礼,二是響起特定的音樂(lè)。那么我們的代碼可能是這樣的:

def onClick():
    changePage()
    playMusic()

后來(lái)你的需求有變動(dòng)并鸵,還需要記錄用戶(hù)點(diǎn)擊時(shí)間鸳粉,需要彈出提示信息,需要......
于是你需要不斷的修改上述的代碼园担,可能是這樣的

def onClick():
    changePage()
    playMusic()
    recordTime()
    popupHint()
    ...

我們認(rèn)為onClick()和這些功能函數(shù)耦合的過(guò)于緊密了届谈,每次的改動(dòng)都會(huì)影響onClick函數(shù)枯夜。這類(lèi)問(wèn)題抽象出來(lái)就是當(dāng)一個(gè)事件發(fā)生時(shí)需要調(diào)用很多功能模塊,而解決這類(lèi)問(wèn)題最好的方式就是觀(guān)察者模式艰山,也叫發(fā)布-訂閱模式湖雹。

是什么

觀(guān)察者模式是說(shuō)你有一個(gè)觀(guān)察者列表,這個(gè)列表中的函數(shù)或者某種功能都在觀(guān)察某個(gè)事件的發(fā)生曙搬,一旦發(fā)生摔吏,這些函數(shù)或者功能就會(huì)自動(dòng)執(zhí)行,這個(gè)其實(shí)很好理解纵装,如下圖:

publisher_subscriber.jpg

我們還是上代碼:

class Button(object):
    """publisher or subject"""
    def __init__(self):
        self.observer_list = []

    def register(self, func):
        self.observer_list.append(func)

    def unregister(self, func):
        self.observer_list.remove(func)

    def onClick():
        for func in self.observer_list:
            func()
    ...

def playMusic():
     """subscriber or observer"""
    ...

def changePage():
    """subscriber or observer"""
    ...
...

def main():
    button = Button()
    button.register(playMusic)
    button.register(changePage)
    ...

通過(guò)這種方式征讲,我們實(shí)現(xiàn)了發(fā)布者和訂閱者之間的松耦合,它們之間并不直接聯(lián)系橡娄,而是通過(guò)統(tǒng)一的register/unregister來(lái)綁定和解綁定诗箍。

怎么用

通過(guò)上面的代碼,細(xì)心的同學(xué)可能會(huì)觀(guān)察到似乎代碼也沒(méi)少啊挽唉,而且注冊(cè)的時(shí)候不還是要修改代碼去注冊(cè)新的功能嗎滤祖?能想到這的同學(xué),你的批判性思維很好瓶籽,鼓勵(lì)一下匠童!
這里基于以下幾種好處我們要使用這個(gè)模式:

  • 使用這個(gè)模式的最大好處之一就是靈活
    我們可以動(dòng)態(tài)的修改監(jiān)聽(tīng)的事件,比如用戶(hù)不想在點(diǎn)擊該按鈕的時(shí)候響起音樂(lè)塑顺,那么當(dāng)ta不選擇這一項(xiàng)時(shí)汤求,我們的程序可以靈活的通過(guò)unregister來(lái)解綁定,試想如果你不使用觀(guān)察者模式茬暇,是很難解綁定的首昔,你就需要去修改代碼,顯然這不現(xiàn)實(shí)糙俗。
# when user uncheck play music option
button.unregister(playMusic)

所以勒奇,使用這個(gè)模式的理由之一就是你需要?jiǎng)討B(tài)的綁定和解綁相應(yīng)的功能時(shí),你就需要觀(guān)察者模式巧骚。

  • 第二個(gè)好處是代碼復(fù)用
    比如你有很多個(gè)按鈕赊颠,都需要統(tǒng)一的注冊(cè)某些功能,這個(gè)時(shí)候你就可以實(shí)現(xiàn)一個(gè)父類(lèi)劈彪,在初始化的時(shí)候?qū)⑺械男枰?cè)的功能都注冊(cè)好竣蹦,子類(lèi)直接繼承就好了,子類(lèi)當(dāng)然還可以注冊(cè)自己特殊功能沧奴《焕ǎ可能的代碼如下:
class Button(object):
    def __init__(self):
        self.observer_list = []
        self.observer_list.register(playMusic)
        self.observer_list.register(popupHint)
    
    def register(self, func):
        self.observer_list.append(func)
    
    def unregister(self, func):
        self.observer_list.remove(func)

    def onClick():
        for func in self.observer_list:
            func()


class ButtonA(Button):
    def __init__(self):
        super(ButtonA, self).__init__()
    ...

class ButtonB(Button):
    def __init__(self):
        super(ButtonA, self).__init__()
        self.observer_list.register(changePage)
    ...

好了,觀(guān)察者模式到這里你就應(yīng)該很清楚了,如果你覺(jué)得有收獲纲菌,不妨點(diǎn)個(gè)贊挠日,如果你覺(jué)得非常贊,那就打個(gè)賞翰舌,鼓勵(lì)是一種美德嚣潜!??

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市椅贱,隨后出現(xiàn)的幾起案子懂算,更是在濱河造成了極大的恐慌,老刑警劉巖庇麦,帶你破解...
    沈念sama閱讀 212,029評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件计技,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡女器,警方通過(guò)查閱死者的電腦和手機(jī)酸役,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,395評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)住诸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)驾胆,“玉大人,你說(shuō)我怎么就攤上這事贱呐∩ヅ担” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,570評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵奄薇,是天一觀(guān)的道長(zhǎng)驳阎。 經(jīng)常有香客問(wèn)我,道長(zhǎng)馁蒂,這世上最難降的妖魔是什么呵晚? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,535評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮沫屡,結(jié)果婚禮上饵隙,老公的妹妹穿的比我還像新娘。我一直安慰自己沮脖,他們只是感情好金矛,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,650評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著勺届,像睡著了一般驶俊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上免姿,一...
    開(kāi)封第一講書(shū)人閱讀 49,850評(píng)論 1 290
  • 那天饼酿,我揣著相機(jī)與錄音,去河邊找鬼胚膊。 笑死故俐,一個(gè)胖子當(dāng)著我的面吹牛奈应,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播购披,決...
    沈念sama閱讀 39,006評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼杖挣,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了刚陡?” 一聲冷哼從身側(cè)響起惩妇,我...
    開(kāi)封第一講書(shū)人閱讀 37,747評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎筐乳,沒(méi)想到半個(gè)月后歌殃,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,207評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蝙云,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,536評(píng)論 2 327
  • 正文 我和宋清朗相戀三年氓皱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片勃刨。...
    茶點(diǎn)故事閱讀 38,683評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡波材,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出身隐,到底是詐尸還是另有隱情廷区,我是刑警寧澤,帶...
    沈念sama閱讀 34,342評(píng)論 4 330
  • 正文 年R本政府宣布贾铝,位于F島的核電站隙轻,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏垢揩。R本人自食惡果不足惜玖绿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,964評(píng)論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望叁巨。 院中可真熱鬧斑匪,春花似錦、人聲如沸俘种。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,772評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)宙刘。三九已至苍姜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間悬包,已是汗流浹背衙猪。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,004評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人垫释。 一個(gè)月前我還...
    沈念sama閱讀 46,401評(píng)論 2 360
  • 正文 我出身青樓丝格,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親棵譬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子显蝌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,566評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容

  • 接觸前端兩三個(gè)月的時(shí)候,那時(shí)候只是聽(tīng)說(shuō)設(shè)計(jì)模式很重要订咸,然后我就去讀了一本設(shè)計(jì)模式的書(shū)曼尊,讀了一部分,也不知道這些設(shè)計(jì)...
    艱苦奮斗的侯小憨閱讀 3,033評(píng)論 2 39
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,799評(píng)論 25 707
  • 設(shè)計(jì)模式基本原則 開(kāi)放-封閉原則(OCP)脏嚷,是說(shuō)軟件實(shí)體(類(lèi)骆撇、模塊、函數(shù)等等)應(yīng)該可以拓展父叙,但是不可修改神郊。開(kāi)-閉原...
    西山薄涼閱讀 3,777評(píng)論 3 14
  • 我只是個(gè)孩子,不一樣的孩子趾唱。 最近開(kāi)始喜歡上了泰戈?duì)柕脑?shī)集涌乳,會(huì)沉浸在他的新月王國(guó)而忘了現(xiàn)世活。仿佛有一種不可測(cè)的魔...
    回戶(hù)閱讀 525評(píng)論 7 5
  • 早起 羽毛球一小時(shí) 追劇 記賬完成
    饞小周閱讀 130評(píng)論 0 0