開篇仍是引用呂振宇老師的那篇經典的文章《設計模式隨筆-蠟筆與毛筆的故事》。這個真是太經典了,沒有比這個例子能更好的闡明橋接模式了,這裏我就直接盜來用了。html
如今市面上賣的蠟筆不少,各類型號,各類顏色種類繁多, 假如一盒蠟筆有24種顏色,那麼它能塗抹出24種不一樣的顏色來,蠟筆型號是固定的,若是想畫出各類線條那麼就要購買不一樣型號的蠟筆,假如咱們要塗抹出粗,中,細三種線條,那麼咱們就要買3盒粗,中,細型號的蠟筆才能知足需求,那麼就是3盒*24色=72只蠟筆。假如使用毛筆來做畫,咱們須要準備3只粗,中,細的毛筆和24種顏料就行了, 那麼就是3只毛筆+24種顏料。使用毛筆和使用蠟筆的差異是:用毛筆只須要3+24=27,用蠟筆須要準備3*24=72. 爲何會出現這麼大的差異呢?仔細分析就會發現,畫畫的時候不只對筆的型號有要求,並且還對顏色有要求,也就是說有兩個引發變化的點或者說有兩個變化的維度。蠟筆的型號和顏色直接綁定在一塊兒了,他們兩者徹底融合(耦合)在一塊兒了,他們的屬性從生產出來就已經固化了(是靜態的),不能被改變了。 而毛筆的型號和顏料的顏色是毫無關係(解耦),毛筆廠家生產不一樣型號的毛筆,顏料廠家生產不一樣顏色的顏料,兩者互不相干,只有在使用的時候用戶決定用什麼型號的毛筆蘸什麼顏色的顏料(動態設置)來做畫,這個時候毛筆和顏料才動態發生關係,若是用戶想使用一個型號的毛筆畫不一樣顏色的畫,毛筆能夠洗掉再蘸不一樣的顏色就能夠。sql
在看看蠟筆和毛筆在應對變化的優劣比較, 若是用戶須要畫一個加粗線條,蠟筆須要買一盒(24),毛筆只須要買一支就能夠了。若是要加一種顏色,蠟筆須要增長4支(加粗,粗,中,細),而毛筆僅僅只須要增長一種顏色就夠了。 從數學的角度來說蠟筆不論是顏色或者型號的變化都會造成:型號數*顏色數,毛筆倒是:型號數+顏色數。這樣看來毛筆更有優點,更容易應對變化的需求。數據庫
那麼在軟件開發的過程當中也會碰到相似的問題,怎麼來解決這類問題呢?這就是咱們將要探討的橋接模式(Brigde)。編程
橋接模式(Bridge Pattern):將抽象部分與它的實現部分分離,使它們均可以獨立地變化。它是一種對象結構型模式,又稱爲柄體(Handle and Body)模式或接口(Interface)模式。設計模式
用於定義抽象類的接口,它通常是抽象類而不是接口,其中定義了一個Implementor(實現類接口)類型的對象並能夠維護該對象,它與Implementor之間具備關聯關係,它既能夠包含抽象業務方法,也能夠包含具體業務方法。ide
擴充由Abstraction定義的接口,一般狀況下它再也不是抽象類而是具體類,它實現了在Abstraction中聲明的抽象業務方法,在RefinedAbstraction中能夠調用在Implementor中定義的業務方法。工具
定義實現類的接口,這個接口不必定要與Abstraction的接口徹底一致,事實上這兩個接口能夠徹底不一樣,通常而言,Implementor接口僅提供基本操做,而Abstraction定義的接口可能會作更多更復雜的操做。Implementor接口對這些基本操做進行了聲明,而具體實現交給其子類。經過關聯關係,在Abstraction中不只擁有本身的方法,還能夠調用到Implementor中定義的方法,使用關聯關係來替代繼承關係。this
具體實現Implementor接口,在不一樣的ConcreteImplementor中提供基本操做的不一樣實現,在程序運行時,ConcreteImplementor對象將替換其父類對象,提供給抽象類具體的業務操做方法。spa
public abstract class Implementor { public abstract void Operation(); } public abstract class Abstraction { protected Implementor implementor; public Implementor Implementor { set { this.implementor = value; } } public virtual void Operation() { implementor.Operation(); } } public class ConcreteImplementorA : Implementor { public override void Operation() { Console.WriteLine(this.GetType().Name + " Operation"); } } public class ConcreteImplementorB : Implementor { public override void Operation() { Console.WriteLine(this.GetType().Name + " Operation"); } } public class RefinedAbstraction : Abstraction { public override void Operation() { base.Operation(); } }
客戶端調用:設計
static void Main(string[] args) { Abstraction abstraction = new RefinedAbstraction(); Implementor implementor = new ConcreteImplementorA(); abstraction.Implementor = implementor; abstraction.Operation(); implementor = new ConcreteImplementorB(); abstraction.Implementor = implementor; abstraction.Operation(); Console.ReadKey(); }
輸出結果:
咱們分別模擬實現開頭提到的蠟筆和毛筆。咱們只選擇三種型號(Large,Middle,Small)和三種顏色(Red,Green, Blue).
蠟筆類代碼:
public abstract class Crayon { protected string size; protected string color; protected abstract void SetSize(); protected abstract void SetColor(); public void Display() { SetSize(); SetColor(); Console.WriteLine(this.GetType().Name + ": [Size]=" + size + "[Color]=" + color); } } public abstract class LargeCrayon : Crayon { protected override void SetSize() { size = "Large"; } } public abstract class MiddleCrayon : Crayon { protected override void SetSize() { size = "Middle"; } } public abstract class SmallCrayon : Crayon { protected override void SetSize() { size = "Small"; } } public class RedLargeCrayon : LargeCrayon { protected override void SetColor() { color = "Red"; } } public class GreenLargeCrayon : LargeCrayon { protected override void SetColor() { color = "Green"; } } public class BlueLargeCrayon : LargeCrayon { protected override void SetColor() { color = "Blue"; } } public class RedMiddleCrayon : MiddleCrayon { protected override void SetColor() { color = "Red"; } } public class GreenMiddleCrayon : MiddleCrayon { protected override void SetColor() { color = "Green"; } } public class BlueMiddleCrayon : MiddleCrayon { protected override void SetColor() { color = "Blue"; } } public class RedSmallCrayon : SmallCrayon { protected override void SetColor() { color = "Red"; } } public class GreenSmallCrayon : SmallCrayon { protected override void SetColor() { color = "Green"; } } public class BlueSmallCrayon : SmallCrayon { protected override void SetColor() { color = "Blue"; } }
客戶端調用:
static void Main(string[] args) { Crayon.Crayon redLargeCrayon, greenLargeCrayon, blueLargeCrayon, redMiddleCrayon, greenMiddleCrayon, blueMiddleCrayon, redSmallCrayon, greenSmallCrayon, blueSmallCrayon; redLargeCrayon = new RedLargeCrayon(); greenLargeCrayon = new GreenLargeCrayon(); blueLargeCrayon = new BlueLargeCrayon(); redMiddleCrayon = new RedMiddleCrayon(); greenMiddleCrayon = new GreenMiddleCrayon(); blueMiddleCrayon = new BlueMiddleCrayon(); redSmallCrayon = new RedSmallCrayon(); greenSmallCrayon = new GreenSmallCrayon(); blueSmallCrayon = new BlueSmallCrayon(); redLargeCrayon.Display(); greenLargeCrayon.Display(); blueLargeCrayon.Display(); redMiddleCrayon.Display(); greenMiddleCrayon.Display(); blueMiddleCrayon.Display(); redSmallCrayon.Display(); greenSmallCrayon.Display(); blueSmallCrayon.Display(); Console.ReadKey(); }
輸出:
蠟筆是一種典型的多層繼承結構, 型號和顏色是在繼承體系中得以實現,在程序編譯的時候型號和顏色就已經綁定好了,在運行時沒法再動態改變,而且類很是多,若是增長型號或者顏色將很是難以維護。
毛筆類結構圖
毛筆類代碼:
public abstract class Brush { private Color color; protected string size; public void SetColor(Color color) { this.color = color; } protected abstract void SetSize(); public void Draw() { SetSize(); Console.WriteLine(this.GetType().Name + ": [Size]=" + this.size + "->[Color]=" + this.color.CurrentColor); } } public abstract class Color { protected string color; public string CurrentColor { get { return color; } } } public class RedColor:Color { public RedColor() { this.color = "Red"; } } public class GreenColor:Color { public GreenColor() { this.color = "Green"; } } public class BlueColor : Color { public BlueColor() { this.color = "Blue"; } } public class LargeBrush : Brush { protected override void SetSize() { this.size = "Large"; } } public class MiddleBrush : Brush { protected override void SetSize() { this.size = "Middle"; } } public class SmallBrush : Brush { protected override void SetSize() { this.size = "Small"; } }
客戶端調用:
static void Main(string[] args) { Brush largeBrush, middleBrush, smallBrush; Color red, green, blue; red = new RedColor(); green = new GreenColor(); blue = new BlueColor(); largeBrush = new LargeBrush(); middleBrush = new MiddleBrush(); smallBrush = new SmallBrush(); largeBrush.SetColor(red); largeBrush.Draw(); largeBrush.SetColor(green); largeBrush.Draw(); largeBrush.SetColor(blue); largeBrush.Draw(); middleBrush.SetColor(red); middleBrush.Draw(); middleBrush.SetColor(green); middleBrush.Draw(); middleBrush.SetColor(blue); middleBrush.Draw(); smallBrush.SetColor(red); smallBrush.Draw(); smallBrush.SetColor(green); smallBrush.Draw(); smallBrush.SetColor(blue); smallBrush.Draw(); Console.ReadKey(); }
輸出結果:
LargeBrush: [Size]=Large->[Color]=Red LargeBrush: [Size]=Large->[Color]=Green LargeBrush: [Size]=Large->[Color]=Blue MiddleBrush: [Size]=Middle->[Color]=Red MiddleBrush: [Size]=Middle->[Color]=Green MiddleBrush: [Size]=Middle->[Color]=Blue SmallBrush: [Size]=Small->[Color]=Red SmallBrush: [Size]=Small->[Color]=Green SmallBrush: [Size]=Small->[Color]=Blue
毛筆類之間的結構發生了一些變化,將蠟筆的深度繼承關係變成了一個平行的關聯關係,這樣帶來的好處是毛筆的型號和顏色能夠在兩個體系中獨立的變化而互不影響。這樣就下降了耦合度,提升了擴展性,和可維護性,使得類的數量也急劇減小,下降了複雜度。
某軟件公司欲開發一個數據轉換工具,能夠將數據庫中的數據轉換成多種文件格式,例如txt、xml、pdf等格式,同時該工具須要支持多種不一樣的數據庫。試使用橋接模式對其進行設計。
可使用橋接模式作一個簡單的實現以下:
public abstract class Database { public abstract string GetData(); } public abstract class Exportor { private Database database; public void SetDatabase(Database database) { this.database = database; } public void Export() { var data = this.database.GetData(); var fileType = this.GetFileType(); Console.WriteLine(this.GetType().Name + "[Database] is [" + data + "] [FileType] is [" + fileType + "]"); } protected abstract string GetFileType(); } public class SQLDatabase : Database { public override string GetData() { return "SQLDatabase"; } } public class OracalDatabase : Database { public override string GetData() { return "OracalDatabase"; } } public class SQLiteDatabase : Database { public override string GetData() { return "SQLiteDatabase"; } } public class DBaseDatabase : Database { public override string GetData() { return "DBaseDatabase"; } } public class ExcelExportor : Exportor { protected override string GetFileType() { return "Excel"; } } public class TxtExportor : Exportor { protected override string GetFileType() { return "TxT"; } } public class XmlExportor : Exportor { protected override string GetFileType() { return "XML"; } } public class PDFExportor : Exportor { protected override string GetFileType() { return "PDF"; } }
客戶端調用:
static void ExecuteExport() { Exportor exportExcel, exportPdf, exportXml, exportTxt; Database sql, sqlite, dbase, oracal; sql = new SQLDatabase(); sqlite = new SQLiteDatabase(); dbase = new DBaseDatabase(); oracal = new OracalDatabase(); exportExcel = new ExcelExportor(); exportPdf = new PDFExportor(); exportTxt = new PDFExportor(); exportXml = new XmlExportor(); exportXml.SetDatabase(sql); exportXml.Export(); exportXml.SetDatabase(oracal); exportXml.Export(); }
輸出:
XmlExportor[Database] is [SQLDatabase] [FileType] is [XML] XmlExportor[Database] is [OracalDatabase] [FileType] is [XML]
抽出一個泛型執行器,使客戶端調用代碼更優雅一點,泛型執行器的代碼以下:
public interface IExportorExcutor<in T, in V> where T : Exportor where V : Database { void Execute(); } public class ExportorExcutor<T, V> : IExportorExcutor<T, V> where T : Exportor, new() where V : Database, new() { public static IExportorExcutor<T, V> Of() { return new ExportorExcutor<T, V>(); } public void Execute() { var export = new T(); var database = new V(); export.SetDatabase(database); export.Export(); } }
客戶端調用代碼:
static void Main(string[] args) { ExportorExcutor<ExcelExportor, SQLiteDatabase>.Of().Execute(); ExportorExcutor<ExcelExportor, OracalDatabase>.Of().Execute(); ExportorExcutor<ExcelExportor, SQLDatabase>.Of().Execute(); ExportorExcutor<ExcelExportor, DBaseDatabase>.Of().Execute(); ExportorExcutor<PDFExportor, SQLiteDatabase>.Of().Execute(); ExportorExcutor<PDFExportor, OracalDatabase>.Of().Execute(); ExportorExcutor<PDFExportor, SQLDatabase>.Of().Execute(); ExportorExcutor<PDFExportor, DBaseDatabase>.Of().Execute(); ExportorExcutor<TxtExportor, SQLiteDatabase>.Of().Execute(); ExportorExcutor<TxtExportor, OracalDatabase>.Of().Execute(); ExportorExcutor<TxtExportor, SQLDatabase>.Of().Execute(); ExportorExcutor<TxtExportor, DBaseDatabase>.Of().Execute(); ExportorExcutor<XmlExportor, SQLiteDatabase>.Of().Execute(); ExportorExcutor<XmlExportor, OracalDatabase>.Of().Execute(); ExportorExcutor<XmlExportor, SQLDatabase>.Of().Execute(); ExportorExcutor<XmlExportor, DBaseDatabase>.Of().Execute(); Console.ReadKey(); }
輸出:
ExcelExportor[Database] is [SQLiteDatabase] [FileType] is [Excel] ExcelExportor[Database] is [OracalDatabase] [FileType] is [Excel] ExcelExportor[Database] is [SQLDatabase] [FileType] is [Excel] ExcelExportor[Database] is [DBaseDatabase] [FileType] is [Excel] PDFExportor[Database] is [SQLiteDatabase] [FileType] is [PDF] PDFExportor[Database] is [OracalDatabase] [FileType] is [PDF] PDFExportor[Database] is [SQLDatabase] [FileType] is [PDF] PDFExportor[Database] is [DBaseDatabase] [FileType] is [PDF] TxtExportor[Database] is [SQLiteDatabase] [FileType] is [TxT] TxtExportor[Database] is [OracalDatabase] [FileType] is [TxT] TxtExportor[Database] is [SQLDatabase] [FileType] is [TxT] TxtExportor[Database] is [DBaseDatabase] [FileType] is [TxT] XmlExportor[Database] is [SQLiteDatabase] [FileType] is [XML] XmlExportor[Database] is [OracalDatabase] [FileType] is [XML] XmlExportor[Database] is [SQLDatabase] [FileType] is [XML] XmlExportor[Database] is [DBaseDatabase] [FileType] is [XML]
橋接模式就探討到這裏。