設計模式之橋接模式

2018-09-20 10:53:13ios

前言

  濫用繼承會帶來麻煩(實際上何止是麻煩,還會帶來性能上的額外開銷,關於繼承的問題,能夠查看繼承相關的文章)。好比,對象的繼承關係是在編譯時就定義好了,因此沒法在運行時改變從父類繼承的實現。子類實現與它的父類有很是緊密的依賴關係。以致於父類中的任何變化必然會致使子類發生變化。當你須要複用子類時,若是繼承下來的實現不適合解決新的問題,則父類必須重寫或被其它更適合的類替換。這種依賴關係限制了靈活性並最終限制了複用性。致使這種問題的緣由是繼承是一種強耦合的關係,是is -a 的關係。設計模式

合成聚合原則

  優先使用對象合成、聚合,而不是類繼承。(想一下兩種模式的適配器,更推薦使用對象適配器就是這個原則的體現了)ide

  合成(Composition,有時也叫組合)和聚合都是關聯的特殊種類。聚合表示一種弱的擁有關係,體現的是A對象能夠包含B對象,可是B對象不是A對象的一部分;合成則是一種強的擁有關係,體現了嚴格的總體和部分的關係,部分和總體的生命週期是同樣的。優先使用對象的聚合、合成將有助於你保持每一個類被封裝,並被集中在單個任務上,這樣類和類的繼承就會保持較小的規模,而且不太可能增加爲不可控制的龐然大物。性能

  聚合和組合關係,這實際上是須要從宏觀上來理解的,而不能從微觀角度理解。組合必然是幾個組件,你們每一個都不能少,少了就不是一個總體,這是一個強擁有的關係。而聚合,多你一個很少,少你一個很多,就像《大話設計模式》裏講的大雁和雁羣的關係,大雁A、大雁B、大雁C、大雁D等無數多隻大雁聚合在一塊兒就是一個雁羣,但是你能說大雁A離開了雁羣這個雁羣就不是雁羣了嗎?顯然不能。可是在具體實現上,其實大都是以類的數據成員來實現的。最多就是在組合的狀況下class內嵌套class,可是這種作法並很差,由於過多的嵌套,可能會形成這個類的臃腫。編碼

橋接模式

  橋接模式(Bridge),將抽象部分與它的實現部分分離,使它們均可以獨立地變化。這裏的抽象與它的實現分離,並非指讓抽象類和派生類分類,這沒有任何意義。實現指的是抽象類和它的派生類用來實現本身的對象。其實就是說,一個系統的實現,可能有多個角度的分類,每一種分類都有可能變化,那麼就把這種多角度分離出來讓它們獨自變化。舉例來講,手機便可以按照品牌分類(手機是抽象,功能是實現),也能夠按照功能分類(功能是實現,而手機變成了抽象)。spa

按照品牌分類實現類圖:設計

  按照功能分類實現結構圖:3d

  能夠看到上面兩種接口圖,模塊與模塊之間有着極強的耦合度,對按照手機品牌分類的結構中,當有新手機品牌加入的時候,你須要爲這一款手機從新開發功實現TaoBao、Game(我知道你會說軟件會兼容,可是這裏暫時不考慮這個,就假設沒有兼容性)。對按照功能來進行分類的時候,每增長一個新功能就要爲對應的品牌獨立開發相應的功能。指針

  實現方式是多種多樣的,而橋接模式的核心意圖就是把實現獨立出來,讓它們各自獨立的變化,而且它們本身的變化不會影響到其它的實現,從而達到應對變化的目的。按照橋接的思路進行設計結構圖以下:code

橋接模式UML類圖

  • Abstraction(抽象類):用於定義抽象類的接口,而且維護一個指向 Implementor 實現類的指針。它與 Implementor 之間具備聚合關係。
  • RefinedAbstraction(擴充抽象類):擴充由 Abstraction 定義的接口,在 RefinedAbstraction 中能夠調用在 Implementor 中定義的業務方法。
  • Implementor(實現類接口):定義實現類的接口,這個接口不必定要與 Abstraction 的接口徹底一致,事實上這兩個接口能夠徹底不一樣。
  • ConcreteImplementor(具體實現類):實現了 Implementor 定義的接口,在不一樣的 ConcreteImplementor 中提供基本操做的不一樣實現。在程序運行時,ConcreteImplementor 對象將替換其父類對象,提供給 Abstraction 具體的業務操做方法。

橋接模式的優缺點

優勢:

  1.橋接分離了抽象和實現的具體聯繫,使得實現和抽象均可以去按照各自的維度去變化,並且相互之間不會產生影響。所謂隔離了抽象和實現,其實就是經過聚合的方式來實現了組件的動態添加和離開,子類不一樣形式的組合可以構建出不一樣的對象。

  2.在設計上它能夠用來代替多層繼承方案。當繼承體系達到了必定深度以後,會致使大量的子類,以及冗餘的功能,而且子類自己的管理也是一個問題。

  3.橋接模式提升了系統的可擴展性,由於抽象和實現,能夠按照各自不一樣的維度進行變化,進而組合出不一樣的類型

缺點:

  1.增長了設計和理解難度,由於關聯關係從一開始就是創建在抽象層面的,意味着編碼工做要針對抽象層進行。

  2.橋接模式要求系統正確的識別各個獨立變化的維度,可是識別這些維度也須要必定的考量。

橋接模式的適用場景

  • 若是一個系統須要在抽象化和具體化之間增長更多的靈活性,避免在兩個層次之間創建靜態的繼承關係,經過橋接模式可使它們在抽象層創建一個關聯關係。
  • 「抽象部分」和「實現部分」能夠以繼承的方式獨立擴展而互不影響,在程序運行時能夠動態將一個抽象化子類的對象和一個實現化子類的對象進行組合,即系統須要對抽象化角色和實現化角色進行動態耦合。
  • 一個系統存在多個(≥ 2)獨立變化的維度,且這多個維度都須要獨立進行擴展。
  • 對於那些不但願使用繼承或由於多層繼承致使系統類的個數急劇增長的系統,橋接模式尤其適用。

代碼示例

  橋接模式使用的關鍵是分清楚誰是實現,誰是抽象。假設咱們有一間很是大的圖書館,館藏豐富,內含n個區域,每一個區域中有不一樣種類的書籍,每一個圖書管理員負責其中一各區域。那圖書管理員則是一個抽象。由於圖書是客觀存在的,它不會由於圖書管理員的離開而消失。。也就是說咱們能夠經過實現的子類和抽象的子類進行組合,可得出任意形狀的系統。使用橋接模式。圖書區內上架什麼書或者下架什麼書,對圖書管理員不會產生影響(只要保持方法穩定)。

1.實現的基類(UML類圖中的Implementor)

#ifndef IMPLEMENTOR_H_
#define IMPLEMENTOR_H_

class Implementor
{
public:
    virtual void display()=0;
    Implementor() = default;
    virtual ~Implementor() = default;
};
#endif
Abstract Implementor

2.實現的具體類(UML類圖中的ConcreteImplementor)

#ifndef IMPLEMENTOR1_H_
#define IMPLEMENTOR1_H_

#include "Implementor.h"
#include <iostream>
class Implementor1:public Implementor
{
public:
    void display() override;
    Implementor1() = default;
    ~Implementor1() = default;
};
#endif

#include "Implementor1.h"

void Implementor1::display()
{
    std::cout << "Book1 " << std::endl;
}
Concrete Implementor

3.抽象的基類(UML類圖中的Abstraction)

#ifndef ABSTRACT_H_
#define ABSTRACT_H_

#include "Implementor.h"
class Abstract
{
public:
    virtual void sell() = 0;
    Abstract() = default;
    virtual ~Abstract() = default;
protected:
    Implementor* m_pobjImplementor{nullptr};
};

#endif
Abstract

4.抽象的具體類(UML類圖中的RefinedAbstraction)

#ifndef REFINEDABSTRACTION1_H_
#define REFINEDABSTRACTION1_H_

#include "Abstract.h"
#include <string>
#include <iostream>

class RefinedAbstraction1:public Abstract
{
public:
    void sell() override;
    RefinedAbstraction1(std::string strName,Implementor* objImplementor): m_strName(strName)
    {
    m_pobjImplementor = objImplementor;
    }
private:
    std::string m_strName;
};
#endif

#include "RefinedAbstraction1.h"

void RefinedAbstraction1::sell()
{
    if(nullptr != m_pobjImplementor)
    {
    std::cout << "My name is:" << m_strName<<".I am responsible for " << std::endl;
        m_pobjImplementor->display();
    }
}

5.客戶端代碼(main)

#include "RefinedAbstraction1.h"
#include "Implementor1.h"
#include "Abstract.h"

int main(int argc,char *aargv[])
{
    Implementor1 objConcreteImplement;
    RefinedAbstraction1 concreteAbstraction("Yang",&objConcreteImplement);
    Abstract *ab = &concreteAbstraction;
    ab->sell();
    return (1);
}
Client
相關文章
相關標籤/搜索