C#設計模式系列:橋接模式(Bridge)

libingql發表於2013-12-29

1、橋接模式簡介

1.1>、定義

  當一個抽象可能有多個實現時,通常用繼承來進行協調。抽象類定義對該抽象的介面,而具體的子類則用不同的方式加以實現。繼承機制將抽象部分與它的實現部分固定在一起,使得難以對抽象部分和實現部分獨立地進行修改、擴充和重用。

  如果一個抽象類或介面有多個具體實現子類,而這些子類之中有內容或概念上重疊,需要我們把抽象的共同部分各自獨立開來:即原來是準備放在一個介面裡,現在需要設計兩個介面——抽象介面和行為介面。然後再分別針對各自的具體子類定義抽象介面和行為介面的方法和呼叫關係。

 

  橋接模式的用意是將抽象化(Abstraction)與實現化(Implementation)脫耦,使得二者可以獨立地變化。

  抽象化(Abstraction)
  存在於多個實體中的共同的概念性聯絡,即為抽象化。作為一個過程,抽象化就是忽略一些資訊,從而把不同的實體當做同樣的實體對待。

  實現化(Implementation)

  抽象化給出的具體實現,即為實現化。

  脫耦
  耦合是指兩個物件的行為的某種強關聯,脫耦則是要去掉它們之間的強關聯。在這裡,脫耦是指將抽象化和實現化之間的耦合解脫,或者將它們之間的強關聯改換成弱關聯。將兩者之間的繼承關係改為聚合關係,就是將它們之間的強關聯改換成為弱關聯。
  橋接模式中的脫耦,是指抽象化和實現化之間使用組合/聚合關係,而不是繼承關係,從而使兩者可以相對獨立地變化。

1.2>、使用頻率

   中等

2、橋接模式結構圖

  橋接模式的結構包括Abstraction、RefinedAbstraction、Implementor、ConcreteImplementorA和ConcreteImplementorB五個部分,其中:

  ◊ Abstraction:定義抽象類的介面,它維護了一個指向Implementor型別物件的指標。

  ◊ RefinedAbstraction:擴充由Abstraction定義的介面;

  ◊ Implementor:定義實現類的介面,該介面不一定要與Abstraction的介面完全一致,事實上兩個介面可以完全不同。一般情況,Implementor介面僅為提供基本操作,而Abstraction則定義了基於基本操作的較高層次操作。

  ◊ ConcreteImplementorAConcreteImplementorB:實現Implementor介面並定義它的具體實現。

  在橋接模式中,兩個類Abstraction和Implementor分別定義了抽象與行為型別的介面,通過呼叫兩介面的子類實現抽象與行為的動態組合。

3、橋接模式結構實現

  Implementor.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DesignPatterns.BridgePattern.Structural
{
    public abstract class Implementor
    {
        public abstract void Operation();
    }
}

  Abstraction.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DesignPatterns.BridgePattern.Structural
{
    public class Abstraction
    {
        protected Implementor implementor;

        public Implementor Implementor
        {
            set
            {
                implementor = value;
            }
        }

        public virtual void Operation()
        {
            implementor.Operation();
        }
    }
}

  ConcreteImplementorA.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DesignPatterns.BridgePattern.Structural
{
    public class ConcreteImplementorA : Implementor
    {
        public override void Operation()
        {
            Console.WriteLine("ConcreteImplementorA Operation");
        }
    }
}

  ConcreteImplementorB.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DesignPatterns.BridgePattern.Structural
{
    public class ConcreteImplementorB : Implementor
    {
        public override void Operation()
        {
            Console.WriteLine("ConcreteImplementorB Operation");
        }
    }
}

  RefinedAbstraction.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DesignPatterns.BridgePattern.Structural
{
    public class RefinedAbstraction : Abstraction
    {
        public override void Operation()
        {
            implementor.Operation();
        }
    }
}

  Client.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DesignPatterns.BridgePattern.Structural
{
    public class Client
    {
        static void Main(string[] args)
        {
            Abstraction abstraction = new RefinedAbstraction();

            abstraction.Implementor = new ConcreteImplementorA();
            abstraction.Operation();

            abstraction.Implementor = new ConcreteImplementorB();
            abstraction.Operation();
        }
    }
}

  執行結果:

ConcreteImplementorA Operation
ConcreteImplementorB Operation
請按任意鍵繼續. . .

4、橋接模式實際應用

  以一杯咖啡為例,子類有四個:中杯加奶、大杯加奶、中杯不加奶、大杯不加奶。這四個類實際是兩個角色的組合:抽象和行為。其中抽象為中杯和大杯,行為為加奶和不加奶。這種從分離抽象和行為的角度的方法稱為橋接模式。

  MakeCoffee.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DesignPatterns.BridgePattern.Practical
{
    public abstract class MakeCoffee
    {
        public abstract void Making();
    }
}

  MakeCoffeeSingleton.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DesignPatterns.BridgePattern.Practical
{
    /// <summary>
    /// 單件模式類用來載入當前MakeCoffee
    /// </summary>
    public sealed class MakeCoffeeSingleton
    {
        private static MakeCoffee _instance;

        public MakeCoffeeSingleton(MakeCoffee instance)
        {
            _instance = instance;
        }

        public static MakeCoffee Instance()
        {
            return _instance;
        }
    }
}

  Coffee.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DesignPatterns.BridgePattern.Practical
{
    public abstract class Coffee
    {
        private MakeCoffee _makeCoffee;

        public Coffee()
        {
            _makeCoffee = MakeCoffeeSingleton.Instance();
        }

        public MakeCoffee MakeCoffee()
        {
            return this._makeCoffee;
        }

        public abstract void Make();
    }
}

  BlackCoffee.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DesignPatterns.BridgePattern.Practical
{
    /// <summary>
    /// 原味咖啡
    /// </summary>
    public class BlackCoffee : MakeCoffee
    {
        public override void Making()
        {
            Console.WriteLine("原味咖啡");
        }
    }
}

  WhiteCoffee.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DesignPatterns.BridgePattern.Practical
{
    /// <summary>
    /// 牛奶咖啡
    /// </summary>
    public class WhiteCoffee : MakeCoffee
    {
        public override void Making()
        {
            Console.WriteLine("牛奶咖啡");
        }
    }
}

  MediumCupCoffee.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DesignPatterns.BridgePattern.Practical
{
    /// <summary>
    /// 中杯
    /// </summary>
    public class MediumCupCoffee : Coffee
    {
        public override void Make()
        {
            MakeCoffee makeCoffee = this.MakeCoffee();
            Console.Write("中杯");
            makeCoffee.Making();
        }
    }
}

  LargeCupCoffee.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DesignPatterns.BridgePattern.Practical
{
    /// <summary>
    /// 大杯
    /// </summary>
    public class LargeCupCoffee : Coffee
    {
        public override void Make()
        {
            MakeCoffee makeCoffee = this.MakeCoffee();
            Console.Write("大杯");
            makeCoffee.Making();
        }
    }
}

  Client.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DesignPatterns.BridgePattern.Practical
{
    class Client
    {
        static void Main(string[] args)
        {
            MakeCoffeeSingleton whiteCoffeeSingleton = new MakeCoffeeSingleton(new WhiteCoffee());

            // 中杯牛奶咖啡
            MediumCupCoffee mediumWhiteCoffee = new MediumCupCoffee();
            mediumWhiteCoffee.Make();

            // 大杯牛奶咖啡
            LargeCupCoffee largeCupWhiteCoffee = new LargeCupCoffee();
            largeCupWhiteCoffee.Make();

            MakeCoffeeSingleton blackCoffeeSingleton = new MakeCoffeeSingleton(new BlackCoffee());
            // 中杯原味咖啡
            MediumCupCoffee mediumBlackCoffee = new MediumCupCoffee();
            mediumBlackCoffee.Make();

            // 大杯牛奶咖啡
            LargeCupCoffee largeCupBlackCoffee = new LargeCupCoffee();
            largeCupBlackCoffee.Make();
        }
    }
}

  執行結果:

中杯牛奶咖啡
大杯牛奶咖啡
中杯原味咖啡
大杯原味咖啡
請按任意鍵繼續. . .

5、橋接模式應用分析

  橋接模式可以適用於以下情形:

  ◊ 不希望在抽象與實現部分之間有固定的繫結關係;

  ◊ 類的抽象以及它的實現都應該可以通過生成子類的方法加以擴充。這時橋接模式可以對不同的抽象介面和實現部分進行組合,並分別對它們進行擴充;

  ◊ 對抽象的實現部分進行修改應對客戶不產生影響,即客戶的程式碼不必重新編譯;

  ◊ 想對客戶完全隱藏抽象的實現部分;

  ◊ 想在多個物件間共享實現,但同時要求客戶並不知道這點。

  橋接模式具有以下特點:

  ◊ 分離介面及其實現部分,一個實現未必不變地繫結在一個介面上。抽象類的實現可以在執行時刻進行配置,一個物件甚至可以在執行時刻改變它的實現;

  ◊ 將Abstraction與Implementor分離有助於降低對實現部分編譯時刻的依賴性;當改變一個實現類時,並不需要重新編譯Abstraction類和Client類。為了保證一個類庫的不同版本之間的相容,需要有這個特性;

  ◊ 介面與實現分離有助於分層,從而產生更好的結構化系統。系統的高層部分僅需要知道Abstraction和Implementor即可;

  ◊ 提高可擴充性。可以獨立的對Abstraction和Implementor層次結構進行擴充;

  ◊ 實現細節對Client透明。可以對Client隱藏實現細節,如共享Implementor物件以及相應的引用計數機制。

相關文章