1. 程式人生 > >設計模式的征途—8.橋接(Bridge)模式

設計模式的征途—8.橋接(Bridge)模式

在現實生活中,我們常常會用到兩種或多種型別的筆,比如毛筆和蠟筆。假設我們需要大、中、小三種類型的畫筆來繪製12中不同的顏色,如果我們使用蠟筆,需要準備3*12=36支。但如果使用毛筆的話,只需要提供3種型號的毛筆,外加12個顏料盒即可,涉及的物件個數僅為3+12=15,遠遠小於36卻能實現與36支蠟筆同樣的功能。如果需要新增一種畫筆,並且同樣需要12種顏色,那麼蠟筆需要增加12支,而毛筆卻只需要新增1支。通過分析,在蠟筆中,顏色和型號兩個不同的變化維度耦合在一起,無論對其中任何一個維度進行擴充套件,都勢必會影響另外一個維度。但在毛筆中,顏色和型號實現了分離,增加新的顏色或者型號都對另外一方沒有任何影響。在軟體系統中,有些型別由於自身的邏輯,它具有兩個或多個維度的變化。為了解決這種多維度變化,又不引入複雜度,這就要使用今天介紹的Bridge橋接模式。

橋接模式(Bridge) 學習難度:★★★☆☆ 使用頻率:★★★☆☆

一、跨平臺的影象瀏覽系統

1.1 需求介紹

M公司開發部想要開發一個跨平臺的影象瀏覽系統,要求該系統能夠顯示JPG、BMP、GIF、PNG等多種格式的檔案,並且能夠在Windows、Linux以及Unix等多個作業系統上執行。該系統首先將各種格式的檔案解析為畫素矩陣(Matrix),然後將畫素矩陣顯示在螢幕上,在不同的作業系統中可以呼叫不同的繪製函式來繪製畫素矩陣。該系統需要具備較好的擴充套件性以支援新的檔案格式和作業系統。

1.2 初始設計

  M公司開發部的程式猿針對需求,立馬提出了一個初始的設計方案,其基本結構如下圖所示:

  通過對這個設計方案的分析,發現存在以下兩個主要問題:

  (1)由於採用了多重繼承結構,導致系統中類的個數急劇增加,系統中類的個數達到了17個。

  (2)系統擴充套件麻煩,由於每一個具體類既包括影象檔案格式資訊,又包含作業系統資訊,因此無論增加新影象檔案格式還是新的作業系統,都需要增加大量的具體類。這將導致系統變得非常龐大,增加執行和維護開銷。

1.3 多維度變化

  通過分析可以知道,這個系統存在兩個獨立變化的維度:影象檔案格式和作業系統,如下圖所示:

  如何將各種不同型別的影象檔案解析為畫素矩陣與影象檔案格式本身相關,而如何在螢幕上繪製畫素矩陣又與作業系統相關。因為初始設計中將這兩種職責集中在一個類中,導致系統擴充套件麻煩,從類的設計角度分析,具體類BMPWindowsImage、BMPLinuxImage、BMPUnixImage等類違反了單一職責原則,因為有不止一個引起它們變化的原因,將影象檔案解析和畫素矩陣顯示這兩種完全不同的職責耦合在一起,任意一個職責發生變化都需要修改它們,因此係統擴充套件十分麻煩。

二、橋接模式簡介

2.1 模式概述

  橋接模式是一種很實用的結構型模式,如果軟體系統中某個類存在兩個獨立變化的維度,通過該模式可以將這兩個維度分離出來,使兩者可以獨立擴充套件,讓系統更加符合單一職責原則。橋接模式主要使用抽象關聯取代傳統的多重繼承,將類之間的靜態繼承關係轉換為動態地物件組合關係,使得系統更加靈活,並易於擴充套件,同時有效地控制了系統中類的個數

  橋接模式的定義如下:

橋接(Bridge)模式:將抽象部分與其實現部分分離,使得他們都可以獨立地變化。它是一種物件結構型模式,又稱為介面模式。

2.2 模式結構

  橋接模式的結構與其名稱一樣,存在一條連線兩個繼承等級結構的橋,橋接模式結果如下所示:

  (1)Abstraction(抽象類):用於定義抽象類的介面,其中定義了一個實現了Implementor介面的物件並可以維護該物件,它與Implementor之間具有關聯關係,它既可以包含抽象業務方法, 也可以包含具體業務方法。

  (2)RefinedAbstratction(擴充抽象類):擴充由Abstraction定義的介面,通常情況下他不再是抽象類而是具體類,實現了在Abstraction中宣告的抽象業務方法,在RefinedAbstraction中可以呼叫在Implementor中定義的業務方法。

  (3)Implementor(實現類介面):定義實現類的介面,一般而言,它不與Abstraction的介面一致。它只提供基本操作,而Abstraction定義的介面可能會做更多更復雜的操作。

  (4)ConcreteImplementor(具體實現類):具體實現Implementor介面,在不同的ConcreteImplementor中提供基本操作的不同實現,在程式執行時,ConcreteImplentor將替換其父類物件,提供給抽象類具體的業務操作方法。

2.3 使用橋接模式

  要使用橋接模式,首先應該識別出一個類所具有的兩個獨立變化的維度,將他們設計成兩個獨立的繼承等級結構,為兩個維度都提供抽象層,並建立抽象耦合。

  這裡我們看一個例子,最開始我們提到毛筆,對於它而言,型號是其固有的維度,因此可以設計一個抽象的毛壁壘,在該類中宣告並部分實現毛筆的業務方法,而將各種型號的毛筆作為其子類;顏色是毛筆的另一個維度,由於它與毛筆之間存在一種“設定”的關係,因此可以提供一個抽象的顏色介面,而將具體的顏色作為實現該介面的子類。在此,型號可以認為是毛筆的抽象部分,而顏色是毛筆的實現部分,其結構示意圖如下:

三、重構跨平臺影象瀏覽系統

3.1 重構設計

  為了減少所需生成的子類數目,實現將作業系統和影象檔案格式兩個維度的分離,使他們可以獨立改變,M公司開發人員決定使用橋接模式來重構設計方案,其基本示意圖如下所示:

3.2 程式碼實現

  (0)輔助類

    public class Matrix
    {
        // 此處程式碼省略
    }

  (1)抽象類

    /// <summary>
    /// 抽象影象類:抽象類
    /// </summary>
    public abstract class Image
    {
        protected ImageImplementor imageImpl;

        public void SetImageImplementor (ImageImplementor imageImpl)
        {
            this.imageImpl = imageImpl;
        }

        public abstract void ParstFile(string fileName);
    }

  (2)擴充抽象類

    public class JPGImage : Image
    {
        public override void ParstFile(string fileName)
        {
            // 模擬解析JPG檔案並獲得一個畫素矩陣物件m
            Matrix m = new Matrix();
            imageImpl.DoPaint(m);
            Console.WriteLine("{0} : 格式為JPG", fileName);
        }
    }

    public class BMPImage : Image
    {
        public override void ParstFile(string fileName)
        {
            // 模擬解析BMP檔案並獲得一個畫素矩陣物件m
            Matrix m = new Matrix();
            imageImpl.DoPaint(m);
            Console.WriteLine("{0} : 格式為BMP", fileName);
        }
    }

    public class GIFImage : Image
    {
        public override void ParstFile(string fileName)
        {
            // 模擬解析GIF檔案並獲得一個畫素矩陣物件m
            Matrix m = new Matrix();
            imageImpl.DoPaint(m);
            Console.WriteLine("{0} : 格式為GIF", fileName);
        }
    }

  (3)實現類介面

    /// <summary>
    /// 抽象作業系統實現類:實現類介面
    /// </summary>
    public interface ImageImplementor
    {
        // 顯示畫素矩陣
        void DoPaint(Matrix m);
    }

  (4)具體實現類

    public class WindowsImplementor : ImageImplementor
    {
        public void DoPaint(Matrix m)
        {
            // 呼叫Windows的繪製函式繪製畫素矩陣
            Console.WriteLine("在Windows系統中顯示影象");
        }
    }

    public class LinuxImplementor : ImageImplementor
    {
        public void DoPaint(Matrix m)
        {
            // 呼叫Linux的繪製函式繪製畫素矩陣
            Console.WriteLine("在Linux系統中顯示影象");
        }
    }

    public class UnixImplementor : ImageImplementor
    {
        public void DoPaint(Matrix m)
        {
            // 呼叫Unix的繪製函式繪製畫素矩陣
            Console.WriteLine("在Unix系統中顯示影象");
        }
    }

  (5)客戶端測試

    public class Program
    {
        public static void Main(string[] args)
        {
            Image image = (Image) AppConfigHelper.GetImageInstance();
            ImageImplementor imageImpl = (ImageImplementor)AppConfigHelper.GetEnvInstance();

            image.SetImageImplementor(imageImpl);
            image.ParstFile("小龍女");

            Console.ReadKey();
        }
    }

  這裡為了讓系統具有更好的靈活性和可擴充套件性,引入了以下配置檔案,將具體擴充抽象類和具體實現類類名都存在了配置檔案中,再通過AppConfigHelper類進行反射生成物件。其中,配置檔案定義如下:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="RefinedAbstraction" value="Manulife.ChengDu.DesignPattern.Bridge.JPGImage, Manulife.ChengDu.DesignPattern.Bridge" />
    <add key="ConcreteImplementor" value="Manulife.ChengDu.DesignPattern.Bridge.LinuxImplementor, Manulife.ChengDu.DesignPattern.Bridge" />
  </appSettings>
</configuration>

  用於讀取配置檔案並反射生成物件的AppConfigHelper類的程式碼如下:

    public class AppConfigHelper
    {
        public static string GetImageFormatName()
        {
            string factoryName = null;
            try
            {
                factoryName = System.Configuration.ConfigurationManager.AppSettings["RefinedAbstraction"];
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            return factoryName;
        }

        public static object GetImageInstance()
        {
            string assemblyName = AppConfigHelper.GetImageFormatName();
            Type type = Type.GetType(assemblyName);

            var instance = Activator.CreateInstance(type);
            return instance;
        }

        public static string GetEnvName()
        {
            string factoryName = null;
            try
            {
                factoryName = System.Configuration.ConfigurationManager.AppSettings["ConcreteImplementor"];
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            return factoryName;
        }

        public static object GetEnvInstance()
        {
            string assemblyName = AppConfigHelper.GetEnvName();
            Type type = Type.GetType(assemblyName);

            var instance = Activator.CreateInstance(type);
            return instance;
        }
    }
View Code

  編譯後執行,輸出結果如下:

  

  由於配置檔案設定的作業系統是Linux,圖片格式是JPG,所以輸出上圖。

  這時,如果我們將配置檔案改為Windows和GIF,會輸出下圖所示:

  

四、橋接模式小結

4.1 主要優點

  (1)分離抽象介面及其實現部分 -> 橋接模式使用“物件間的關聯關係”解耦了抽象和實現之間固有的繫結關係,使得抽象和實現可以沿著各自的維度變化

  (2)取代多層繼承方案 -> 極大地減少了子類的個數

  (3)提高了系統可擴充套件性 -> 在兩個變化維度中任意擴充套件一個維度,都不需要修改原有系統,符合開閉原則

4.2 主要缺點

  (1)增加了系統的理解和設計難度 -> 需要開發者在一開始就對抽象層進行設計與程式設計

  (2)要求正確識別出系統中兩個獨立變化的維度 -> 如何正確地識別需要一定的經驗積累

4.3 應用場景

  (1)一個類存在兩個(或者多個)獨立變化的維度,而且這兩個(或者多個)維度都需要獨立進行擴充套件。

  (2)不希望使用繼承或因為多層繼承而導致系統中類的個數急劇增加。

  (3)一個系統需要在抽象類和具體類之間增加更多的靈活性,避免在兩個層次之間建立靜態繼承關係,通過橋接可以使它們在抽象層建立一個關聯關係。

參考資料

  DesignPattern

  劉偉,《設計模式的藝術—軟體開發人員內功修煉之道》

作者:周旭龍

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連結。

相關推薦

設計模式征途8.Bridge模式

在現實生活中,我們常常會用到兩種或多種型別的筆,比如毛筆和蠟筆。假設我們需要大、中、小三種類型的畫筆來繪製12中不同的顏色,如果我們使用蠟筆,需要準備3*12=36支。但如果使用毛筆的話,只需要提供3種型號的毛筆,外加12個顏料盒即可,涉及的物件個數僅為3+12=15,遠遠小於36卻能實現與36支蠟筆同樣的功

設計模式11—— 結構型 ——bridge

介紹 定義:將抽象部分與它的具體實現部分分離,使它們都可以獨立地變化 說明:通過組合的方式建立兩個類之間的聯絡,而不是繼承 型別:結構型 適用場景: 抽象和具體實現之間增加更多的靈活性 一個類存在兩個(或多

設計模式9—— bridge

介紹 定義:將抽象部分與它的具體實現部分分離,使它們都可以獨立地變化 說明:通過組合的方式建立兩個類之間的聯絡,而不是繼承 型別:結構型 適用場景: 抽象和具體實現之間增加更多的靈活性 一個類存在

Vmware虛擬機僅主機Host-onlyBridge、NAT三種網絡模式介紹

主機ip 一個 選擇 17. def roc dcb 相互 兩個 Vmware虛擬機僅主機(Host-only)、橋接(Bridge)、NAT三種網絡模式介紹 給大家介紹一下虛擬機的三種網絡模式,希望能對大家有所幫助。 虛擬機在創建完成以後,會在物理主

Ubuntu14.04+KVM配置虛擬機器bridge

主機:Ubuntu14.04 64bit 虛擬機器:Ubuntu14.04 64bit VMM:KVM Bridge橋接原理原理 KVM安裝後預設的網路連結方式是NAT,此時虛擬機器只能夠與安裝在本機上的其餘虛擬機器通訊,虛擬機器的IP地址是

設計模式征途—23.解釋器Interpreter模式

args 參考資料 轉載 返回 下一個 tle title 缺點 images 雖然目前計算機編程語言有好幾百種,但有時人們還是希望用一些簡單的語言來實現特定的操作,只需要向計算機輸入一個句子或文件,就能按照預定的文法規則來對句子或文件進行解釋。例如,我們想要只輸入一個加法

設計模式征途—22.中介者Mediator模式

我們都用過QQ,它有兩種聊天方式:一是私聊,二是群聊。使用QQ群,一個使用者就可以向多個使用者傳送相同的資訊和檔案,從而無需一一發送,節省大量時間。通過引入群的機制,極大地減少系統中使用者之間的兩兩通訊,使用者與使用者之間的聯絡可以通過群的機制來實現。 在有些軟體中,某些類/物件之間的相互呼叫關係錯綜複

設計模式征途—6.建造者Builder模式

建造者模式又稱為生成器模式,它是一種較為複雜、使用頻率也相對較低的建立型模式。建造者模式為客戶端返回的不是一個簡單的產品,而是一個由多個部件組成的複雜產品。因為,沒有人買車會只買一個方向盤或者輪胎,大家買的都是一輛包含輪胎、方向盤和發動機等多個部件組成的完整汽車。如何將這些部件組裝成一輛完整的汽車並返回給使用

設計模式征途—15.觀察者Observer模式

在日常生活中,交通訊號燈指揮者日益擁擠的城市交通。紅燈亮,汽車停止;綠燈亮,汽車繼續前行;在這個過程中,交通訊號燈是汽車的觀察目標,而汽車則是觀察者。隨著交通訊號燈的變化,汽車的行為也會隨之變化,一盞交通訊號燈可以指揮多輛汽車。在軟體系統中,有些物件之間也存在類似交通訊號燈和汽車之間的關係,一個物件的狀態或行

設計模式征途—1.單例Singleton模式

  單例模式屬於建立型模式的一種,建立型模式是一類最常用的設計模式,在軟體開發中應用非常廣泛。建立型模式將物件的建立和使用分離,在使用物件時無需關心物件的建立細節,從而降低系統的耦合度,讓設計方案更易於修改和擴充套件。每一個建立型模式都在檢視回答3個問題:3W -> 建立什麼(What)、由誰建立(Wh

設計模式征途—12.享元Flyweight模式

現在在大力推行節約型社會,“浪費可恥,節儉光榮”。在軟體系統中,有時候也會存在資源浪費的情況,例如,在計算機記憶體中儲存了多個完全相同或者非常相似的物件,如果這些物件的數量太多將導致系統執行代價過高。那麼,是否存在一種技術可以用於節約記憶體使用空間,實現對這些相同或者相似物件的共享訪問呢?答案是肯定的,這種技

設計模式】module模式&&Revealing module 揭示模式

但是 出版 參數傳遞 9.png 自然 指向 們的 private 初級 寫在前面 《head first設計模式》裏有一篇文章,是說使用模式的心智,   1、初學者"心智" :"我要為HELLO WORLD找個模式"   2、中級人員模式: "或許這裏我需要一個單件

【java設計模式】之 單例Singleton模式

1. 單例模式的定義         單例模式(Singleton Pattern)是一個比較簡單的模式,其原始定義如下:Ensure a class has only one instance, and provide a global point of access

【java設計模式】之 建造者Builder模式

        我們還是舉上一節的例子:生產汽車。上一節我們通過模板方法模式控制汽車跑起來的動作,那麼需求是無止境的,現在如果老闆又增加了額外的需求:汽車啟動、停止、鳴笛引擎聲都由客戶自己控制,他想要什麼順序就什麼順序,那該如何做呢? 1. 汽車無休止的改造       

虛擬機器VMware下CentOSVMnet0連外網

執行環境: win7-32bit / win10-64bit VMware 10 CentOS 6.5 步驟 設定本地連線相關項 控制面板–>網路和Internet–>網路和共享中心–>更改介面卡設定 或者 開啟網

聊一聊JSBridge的原理

##### 一、前言 >如今的網際網路時代也稱移動網際網路時代,基本上每個人每天都會花費大量時間在移動裝置上,早期的移動端應用大都使用原生開發(android,ios),而現在的移動開發技術選型上基本都是混合開發(Hybrid),混合開發是一種開發模式,指使用多種開發模型開發App,通常會涉及到兩大類技術:

程式設計模式(十二) C++ 代理Proxy模式

2.7 Proxy 代理模式為其他物件提供一種代理以控制對這個物件的訪問。 在需要用比較通用和複雜的物件指標代替簡單的的指標的時候,使用代理模式。有四種常用的情況:        1、遠端代理,也就是為一個物件在不同的地址空間提供區域性代表。這樣可以隱藏一個物件存在於不

設計模式之十八:模式Bridge

ora 它的 pla sin string src ams down ng- 橋接模式: 將抽象部分和它的實現部分相分離開來,以使它們能夠單獨地變化。 UML圖: 主要包含: Abstraction:定義了抽象部分的接口。操作一個實現部分對

C#設計模式之八模式Bridge【結構型】

升級 方向 implement 詳細 .cn mage names 這樣的 意圖 一、引言 今天我們要講【結構型】設計模式的第二個模式,該模式是【橋接模式】,也有叫【橋模式】的。大家第一次看到這個名稱會想到什麽呢?我第一次看到這個模式根據名稱猜肯定是連接什麽東西的。因為

C#設計模式系列:模式Bridge

span -i 原來 派生 引用 分享圖片 on() 版本 nta 1.1定義 當一個抽象可能有多個實現時,通常用繼承來進行協調。抽象類定義對該抽象的接口,而具體的子類則用不同的方式加以實現。繼承機制將抽象部分與它的實現部分固定在一起,使得難以對抽象部分和實現部分獨立地進行