github地址:https://github.com/cheesezh/python_design_patternspython
編程模擬如下情景,有一個N品牌手機,在上邊玩一個小遊戲。git
class HandsetNGame(): def run(self): print("運行N品牌手機遊戲") def main(): game = HandsetNGame() game.run() main()
運行N品牌手機遊戲
如今又有一個M品牌的手機,也有小遊戲,客戶端也能夠調用,須要如何改?github
from abc import ABCMeta, abstractmethod class HandsetGame(): __metaclass__ = ABCMeta @abstractmethod def run(self): pass class HandsetNGame(HandsetGame): def run(self): print("運行N品牌手機遊戲") class HandsetMGame(HandsetGame): def run(self): print("運行M品牌手機遊戲")
如今不一樣的手機又要增長通信錄功能,又要如何改?編程
那就意味着父類應該是「手機品牌」,下有「手機品牌M」和「手機品牌N」,每一個子類下邊又各有「通信錄」和「小遊戲」。從上到下是個1-2-4的層級結構。設計模式
若是這樣的話,那增長一款新的軟件,須要在每一個手機品牌下邊都實現一次,若是新增長一款手機品牌,又須要把全部軟件都重寫一次。設計
對象的繼承關係在編譯時就定義好了,因此沒法在運行時改變從父類繼承的實現。子類的實現與它的父類有很是緊密的依賴關係,以致於父類實現中的任何變化都必然會致使子類發生變化。但你須要複用子類時,若是繼承下來的實現不適合解決新的問題,則父類必須重寫或者被其餘更合適的類替換。這種依賴關係限制了靈活性並最終限制了複用性[DP]。code
合成/聚合複用原則,儘可能使用合成/聚合,儘可能不要使用類繼承。對象
聚合表示一種弱的「擁有」關係,體現的是A對象能夠包含B對象,可是B對象不是A對象的一部分,就如同「大雁」和「雁羣」;繼承
合成表示一種強的「擁有」關係,體現了嚴格的部分和總體的關係,部分和總體的生命週期同樣,就如同「吃飽」和「大雁」;生命週期
優先使用對象的合成/聚合將有助於保持每一個類被封裝,並被集中在單個任務上。這樣類和類繼承層次會保持較小規模,而且不太可能增加爲不可控制的龐然大物[DP]。
像「遊戲」,「通信錄」等功能都是軟件,咱們應該讓其與手機解耦,也就是再弄一個「手機軟件」抽象類。
from abc import ABCMeta, abstractmethod class HandsetSoft(): __metaclass__ = ABCMeta @abstractmethod def run(self): pass class HandsetGame(HandsetSoft): def run(self): print("run handset game") class HandsetAddressList(HandsetSoft): def run(self): print("run handset address list") class HandsetBrand(): __metaclass__ = ABCMeta @abstractmethod def run(slef): pass def set_handset_soft(self, soft): self.soft = soft class HandsetBrandN(HandsetBrand): def run(self): print("on handset brand N") self.soft.run() class HandsetBrandM(HandsetBrand): def run(self): print("on handset brand M") self.soft.run() def main(): brand = HandsetBrandN() brand.set_handset_soft(HandsetGame()) brand.run() brand.set_handset_soft(HandsetAddressList()) brand.run() brand = HandsetBrandM() brand.set_handset_soft(HandsetGame()) brand.run() brand.set_handset_soft(HandsetAddressList()) brand.run() main()
on handset brand N run handset game on handset brand N run handset address list on handset brand M run handset game on handset brand M run handset address list
如今若是要增長音樂播放器,那麼只要增長這個類就行,不影響其餘任何類。
class HandsetPlayer(HandsetSoft): def run(self): print("run handset player")
如今若是要增長手機品牌S,也只須要增長一個品牌子類便可。
這顯然也符合了咱們以前的開放-封閉原則,這樣的設計顯然不會修改原來的代碼,只要擴展類便可。
繼承也是一種強耦合的結構,因此優先使用對象的合成或聚合,而不是類繼承。
像「手機軟件」和「手機品牌」兩個抽象類之間進行關聯,使得它們的實現部分互相分類,就好像一座橋同樣,這就是橋接模式。
橋接模式,將抽象部分與它的實現部分分離,使它們能夠獨立地變化。[DP]
將抽象部分和實現部分分離,並非指讓抽象類和派生類分離,而是指抽象類和它的派生類用來實現本身的對象。就像剛剛的手機品牌&手機軟件之間的關係,既能夠按照品牌進行實現,又能夠按照軟件分類進行實現。因爲實現的方式有多種,橋接模式的核心意圖就是把這些實現獨立出來,讓它們各自的變化。這使得每種實現的變化都不會影響其餘實現,從而達到應對變化的目的。
面對真實需求,實現系統可能有多角度分類,每一種分類均可能變化,那麼就把這種多角度分離出來讓它們獨立變化,減小它們之間的耦合。
在咱們須要多角度去分類實現對象,而只用繼承會形成大量的類增長,不能知足開放-封閉原則時,就應該考慮使用橋接模式了。
只要真正深刻理解設計原則,不少設計模式其實就是原則的應用而已,或許在不經意間就在使用設計模式了。