大話設計模式:第22章 橋接模式

第22章:橋接模式

繼承帶來的麻煩

對象的繼承關係是在編譯時就定義好了,所以無法在運行時改變從父類繼承的實現。子類的實現與它的父類有非常緊密的依賴關係,以至於父類實現中的任何變化必然會導致子類發生變化。當需要複用子類時,如果繼承下來的實現不適合解決新的問題,則父類必須重寫或被其他更適合的類替換。這種依賴關係限制了靈活性並最終限制了複用性。

合成、聚合複用原則

合成、聚合複用原則(composition & aggregation reuse principle,CARP),儘量使用合成、聚合,儘量不要使用類繼承。

合成(組合,composition)和聚合(aggregation)都是關聯的特殊種類。

聚合:表示一種弱的“擁有”關係,體現的是A對象可以包含B對象,但B對象不是A對象的一部分;

合成:表示一種強的“擁有”關係,體現了嚴格的部分和整體的關係,部分和整體的生命週期相同。

例:大雁有兩個翅膀,翅膀與大雁是部分和整體的關係,並且它們的生命週期是相同的,於是大雁和翅膀就是合成關係;大雁是羣居動物,所以每隻大雁都是屬於一個雁羣,一個雁羣可以有多隻大雁,所以大雁和雁羣是聚合關係。

合成、聚合複用原則的優點是優先使用對象的合成、聚合將有助於你保持每個類被封裝並被集中在單個任務上。這樣類和類繼承層次會保持較小規模,並且不太可能增長爲不可控制的龐然大物。

緊耦合v.s.鬆耦合

  • 緊耦合(繼承)

在這裏插入圖片描述

  • 鬆耦合(聚合)

在這裏插入圖片描述

鬆耦合代碼

手機軟件抽象類

在這裏插入圖片描述

遊戲、通訊錄等具體類

在這裏插入圖片描述

手機品牌類

在這裏插入圖片描述

品牌N品牌M具體類

在這裏插入圖片描述
客戶端調用代碼

在這裏插入圖片描述

增加MP3音樂播放類

在這裏插入圖片描述

增加品牌S

在這裏插入圖片描述

合成、聚合複用原則:優先使用對象的合成或聚合,而不是類繼承。繼承是一種強耦合的結構,父類變,子類必須變,與合成、聚合相比,繼承容易造成不必要的麻煩。所以使用繼承時,一定要在is-a的關係時再考慮使用。

橋接模式

橋接(bridge)模式:將抽象部分與它的實現部分分離,使它們都可以獨立地變化。

抽象與它的實現分離不是讓抽象類與其派生類分離,因爲這沒有任何意義。實現指的是抽象類和它的派生類用來實現自己的對象。由於實現的方式有多種,橋接模式的核心意圖就是把這些實現獨立出來,讓它們各自地變化。這就使得每種實現的變化不會影響其他實現,從而達到應對變化的目的。

在這裏插入圖片描述

Implementor

在這裏插入圖片描述

ConcreteImplementorAConcreteImplementorB等派生類

在這裏插入圖片描述

Abstraction

在這裏插入圖片描述

RefinedAbstraction

在這裏插入圖片描述

客戶端實現

在這裏插入圖片描述

橋接模式所說的“將抽象部分與它的實現部分分離”是實現系統可能有多角度分類,每一種分類都有可能變化,那麼就把這種多角度分離出來讓它們獨立變化,減少它們之間的耦合。

在需要多角度去分類實現對象時,只用繼承會造成大量的類增加,不滿足開放-封閉原則,此時應該要考慮橋接模式。

橋接模式示例

任務:手機應用(多品牌,多應用)

在這裏插入圖片描述

from abc import ABCMeta, abstractmethod
from typing import Text

class HandsetSoft(metaclass=ABCMeta):
    """
    手機軟件抽象類(Implementor)
    """
    @abstractmethod
    def run(self) -> None:
        pass
    
class HandsetGame(HandsetSoft):
    """
    手機遊戲類
    """
    def run(self) -> None:
        print("運行手機遊戲")
        
class HandsetAddressList(HandsetSoft):
    """
    手機通訊錄類
    """
    def run(self) -> None:
        print("運行手機通訊錄")
class Handset(metaclass=ABCMeta):
    """
    手機品牌類(Abstraction)
    """
    def __init__(self) -> None:
        self._soft = None
        
    def set_handset_soft(self, soft: HandsetSoft) -> None:
        self._soft = soft
        
    @abstractmethod
    def run(self) -> None:
        pass
class HandsetBrandN(Handset):
    """
    手機品牌N
    """
    def run(self) -> None:
        print("手機N")
        self._soft.run()
        
class HandsetBrandM(Handset):
    """
    手機品牌M
    """
    def run(self) -> None:
        print("手機M")
        self._soft.run()
# 客戶端調用代碼

if __name__ == "__main__":
    
    ab = HandsetBrandN()
    
    ab.set_handset_soft(HandsetGame())
    ab.run()
    
    ab.set_handset_soft(HandsetAddressList())
    ab.run()
    
    ab = HandsetBrandM()
    
    ab.set_handset_soft(HandsetGame())
    ab.run()
    
    ab.set_handset_soft(HandsetAddressList())
    ab.run()
手機N
運行手機遊戲
手機N
運行手機通訊錄
手機M
運行手機遊戲
手機M
運行手機通訊錄
class HandsetBrandS(Handset):
    """
    手機品牌S
    """
    def run(self) -> None:
        print("手機S")
        self._soft.run()
        
class HandsetMP3(HandsetSoft):
    """
    手機MP3類
    """
    def run(self) -> None:
        print("運行手機MP3音樂播放器")
# 客戶端調用代碼

if __name__ == "__main__":
    
    ab = HandsetBrandS()
    
    ab.set_handset_soft(HandsetMP3())
    ab.run()
    

手機S
運行手機MP3音樂播放器
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章