在系統開發中,咱們經常能遇到如下的幾種場景:設計模式
1.有一箇舊系統,咱們想要經過重構的方式,對舊系統進行一些升級,但舊系統內部的依賴性和複雜性很高,可謂是「牽一髮而動全身」啊,怎麼將影響減到最小?ide
2.咱們要開發一套新的系統去集成現有的舊系統,舊系統中的代碼和功能不容許改變,新系統如何去適配舊系統的接口完成對接?spa
3.購買了第三方的成熟組件,如何在本身的系統中有效的和第三方組件做接口?設計
要解決以上的這些問題,咱們能夠考慮使用適配器模式來處理。固然你能夠看出來這些場景都是在不得已的狀況下的補救措施,是迫不得已時之舉,正常設計系統時仍是要好好考慮將來的擴展性和可維護性。對象
來看一下適配器模式的標準定義是怎樣的。繼承
適配器模式(Adaptor):將一個類的接口轉換成客戶但願的另一個接口。Adapter 模式使得本來因爲接口不兼容而不能一塊兒工做的那些類能夠一塊兒工做。接口
舉個生活中的例子,你想把一個PS/2接口的鍵盤,外接到筆記本上,怎麼辦呢?衆所周知,筆記本上沒有PS/2接口的,只有USB接口。好在,市面上有一種PS2轉USB的轉接器,兩邊簡單對接就能夠正經常使用了~開發
這其實就是適配器模式的原理,將使兩邊接口統一化。get
下面來看看適配器模式的組成吧。
1) 目標(Target)角色:定義Client 使用的接口。
2) 被適配(Adaptee)角色:這個角色有一個已存在並使用了的接口,而這個接口是須要咱們適配的。
3) 適配器(Adapter)角色:這個適配器模式的核心。它將被適配角色已有的接口轉換爲目標角色但願的接口。博客
適配器從實現方式上能夠分爲兩種:類適配器和對象適配器。二者的區別在於,前者採用繼承,後者採用組合。
首先,咱們來看一下類適配器是個什麼樣的。
咱們有一個新的接口 NewInterface,可是舊系統中的一個類沒有實現這個接口,咱們用繼承這個類,同時實現這個新接口的方法,構造出一個適配器類 Adaptor, Adaptor統一了接口,客戶端能夠構造一個NewInterface類型的Adaptor實例對象了,這個實例對象同時具備舊類的功能,又符合了接口規範。
1 /// <summary> 2 /// 新接口 3 /// </summary> 4 public interface NewInterface 5 { 6 void WriteMessage(); 7 } 8 9 /// <summary> 10 /// 舊類(與新接口沒法對接) 11 /// </summary> 12 public class OldClass 13 { 14 public void Message() 15 { 16 Console.WriteLine("這是舊類的原方法。"); 17 } 18 } 19 20 /// <summary> 21 /// 適配器 22 /// 繼承舊類,並實現新接口 23 /// </summary> 24 public class Adpter : OldClass,NewInterface 25 { 26 public void WriteMessage() 27 { 28 base.Message(); 29 Console.WriteLine("這是適配後的新方法。"); 30 } 31 }
模擬客戶端調用:
1 static void Main(string[] args) 2 { 3 NewInterface a=new Adpter(); 4 a.WriteMessage(); 5 6 Console.ReadKey(); 7 }
執行結果顯示,舊類的方法和新的接口方法都被調用了。
這就是類適配器的實現方法,可是你是否發現了問題呢? 若是我須要一個適配器去包裝多箇舊類怎麼辦?在C#中是不支持類的多重繼承的,因此下面的寫法是沒法經過編譯的:
public class Adpter : OldClass1,OldClass2,OldClass3,NewInterface
若要解決這個問題,咱們能夠考慮另一種實現方式:對象適配器
實現的思路也很簡單,咱們不採用繼承了,只去實現一下新的接口,而後把全部要適配的舊類做爲適配器類的成員對象包含到內部就能夠了。
保持其餘部分的代碼不變,咱們只修改一下Adpter這個類,代碼以下:
1 /// <summary> 2 /// 適配器 3 /// 只實現接口,內部建立舊類的實例對象成員 4 /// </summary> 5 public class Adpter : NewInterface 6 { 7 private OldClass oldClass=new OldClass(); 8 public void WriteMessage() 9 { 10 oldClass.Message(); 11 Console.WriteLine("這是適配後的新方法。"); 12 } 13 }
執行結果是同樣的。
另外,從使用適配器的目的性上講,又能夠分紅特殊適配器和缺省適配器。前者是爲了複用原有代碼並適配當前接口,咱們上面的例子都是特殊適配器。缺省適配器是一種缺省實現的一種方法,也就是避免子類爲了保持繼承的完整性,不得不去實現一些方法,但方法體爲空。
前面說過,適配器實際上是一種補救的方式,那咱們研究一下是什麼緣由致使咱們要用缺省適配器的方法。
在設計模式的六大原則裏有一個叫做「最小接口原則」,就是接口的設計要足夠小,職責要足夠單一,好比一個手機接口包含「打電話」和「發短信」的方法是合理,若是同時包含「打飛機」這個方法,就違背了「最小接口原則」了,由於「打飛機」並非一個手機通用的功能,咱們應該再設計一個智能手機接口去單獨包含它。
這個原則搞明白之後,你確定就知道問題的來源了,沒錯,就是由於接口功能不夠單一,致使接口過大,而繼承他的子類可能並不能提供這樣的功能,只能出現方法空着的狀況了。
好比咱們已經有了以下設計不合理的接口:
1 public interface Phone 2 { 3 //打電話 4 void Call(); 5 //發短信 6 void Message(); 7 //玩打飛機 8 void Plane(); 9 }
下面,好比咱們要構造一個老年機,代碼以下:
1 /// <summary> 2 /// 老年機 3 /// </summary> 4 public class OldPeoplePhone : Phone 5 { 6 public void Call() 7 { 8 Console.WriteLine("老年機能夠打電話"); 9 } 10 11 public void Message() 12 { 13 Console.WriteLine("老年機能夠發短信"); 14 } 15 16 public void Plane() 17 { 18 //老年機打不了飛機,沒有具體實現 19 } 20 }
你看到了Plane()沒有什麼能夠實現的,但爲了繼承接口,這個空方法必須擺在那。
爲了比較好的解決這個問題呢,咱們來看一下缺省適配器如何工做。
1 public class PhoneAdaptor : Phone 2 { 3 public virtual void Call() 4 { 5 //留空,子類若是實現能夠重寫 6 } 7 8 public virtual void Message() 9 { 10 //留空,子類若是實現能夠重寫 11 } 12 13 public virtual void Plane() 14 { 15 //留空,子類若是實現能夠重寫 16 } 17 }
1 /// <summary> 2 /// 老年機 3 /// </summary> 4 public class OldPeoplePhone : PhoneAdaptor 5 { 6 public override void Call() 7 { 8 Console.WriteLine("老年機能夠打電話"); 9 } 10 public override void Message() 11 { 12 Console.WriteLine("老年機能夠發短信"); 13 } 14 }
之後,子類只要繼承自適配器類,去實現本身的方法就能夠了,避免了書寫和保留大量的空方法。
適配器模式是一種補救手段,是爲了複用已有類的代碼而且將其適配到客戶端須要的接口上去。
1.第一種類適配器,因爲C#不支持多重類繼承的方式,因此適用的範圍有限。
2.第二種對象適配器,當要適配的對象多於一個的時候考慮使用。
3.第三種缺省適配器,是爲了彌補接口過大的歷史問題而致使的不得不去實現全部方法,但不少方法只能留空的狀況。
好了,本次適配器模式的分享就到此結束了,若是理解有誤請不吝賜教,共同提升~ 謝謝您的收看。