為什么要把適配器模式和外觀模式放在同一篇文章中,主要是其相對前面的幾個模式來講會簡單些並且具有相似之處。下面就分別通過例子來看理解一下兩種模式,然后再進行對其進行比較。
一、適配器模式
1.1適配器模式的定義
適配器模式定義:將一個類的接口,轉化成客戶期望的另一個接口,適配器讓原本接口不兼容的類可以合作無間。
感覺是從定義就可以看出來怎么實現的設計模式。簡單的來說就是把不符合要求的類,通過實現期望的接口來來達到以假亂真的效果。好了,廢話不多說,還是通過例子來說明。
1.2適配器的例子
最常見的例子是三孔插座和兩孔插座,如果牆上有一個三孔插座,但是我們的充電器又只能使用兩孔的插座,那么我們通常是接一條帶有兩孔和三孔的插座其插頭是三個的,以適應不同的需求。在以上的例子中,我們來簡單分析一下適配器模式中的對象有哪些:
適配器模式的對象:
1.請求對象(手機)
2.適配器對象(帶有兩孔和三孔的插座)
3.需要適配的對象(三孔插座)
4.請求對象所需要的接口。(插座要有兩孔)
接來下就使用代碼實現一下:

1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Mobile mobile = new Mobile(); 6 ThreeHole threeHole = new ThreeHole(); 7 LineWithTwoHole lineWithTwoHole = new LineWithTwoHole(threeHole); 8 mobile.Charge(lineWithTwoHole); 9 Console.ReadKey(); 10 } 11 } 12 13 /// <summary> 14 /// 手機類 15 /// </summary> 16 public class Mobile 17 { 18 public void Charge(ITwoHole twoHole) 19 { 20 twoHole.Connect(); 21 AddPower(); 22 } 23 public void AddPower() 24 { 25 Console.WriteLine("電量增加中。。。。"); 26 } 27 } 28 29 /// <summary> 30 /// 兩孔插座接口 31 /// </summary> 32 public interface ITwoHole 33 { 34 void Connect(); 35 } 36 37 /// <summary> 38 /// 三孔插座 39 /// </summary> 40 public class ThreeHole 41 { 42 public void Connect() 43 { 44 LeftConnect(); 45 RightConnect(); 46 ExtraConnect(); 47 } 48 49 public void LeftConnect() 50 { 51 Console.WriteLine("零線接通中。。。"); 52 } 53 public void RightConnect() 54 { 55 Console.WriteLine("火線接通中。。。。。"); 56 } 57 public void ExtraConnect() 58 { 59 Console.WriteLine("底線接通中。。。。"); 60 } 61 } 62 63 public class LineWithTwoHole:ITwoHole 64 { 65 private ThreeHole threeHole; 66 public LineWithTwoHole(ThreeHole threeHole) 67 { 68 this.threeHole = threeHole; 69 } 70 public void Connect() 71 { 72 threeHole.LeftConnect(); 73 threeHole.RightConnect(); 74 } 75 }
運行結果:
接下來看看適配器模式的類圖:
1.3適配器模式類圖
適配器模式的關系:請求對象引用需要適配的接口,適配器引用需要適配對象,適配器需要通過被適配對象來實現需要適配的接口。
1.4適配器模式需要注意的問題
在三孔插座轉化成兩孔插座時,工作量很小,是只調用了三孔插座的左孔和右孔。但是也有可能是需要很大的目標接口,就會有很多工作要做。也即實現適配器所需要的工作和目標接口的大小成正比。
一個適配器真的只需要一個被適配者嗎?
其實適配的目標是一個完成接口,在需要比較大型的接口時,可能需要多個被適配者才能實現。
二、外觀模式
2.1外觀模式的定義
外觀模式定義:外觀模式提供了一個統一的接口,用來訪問子系統中的一群接口。外觀定義了一個高層接口,讓子系統更容易使用。
外觀模式讓我想起了一套設備,不知道大家有沒有照過大頭貼,我是沒有照過,但是我見過照大頭貼的設備。其是由電腦,打印機,白熾燈,相機組成,基本的操作是:打開電腦,打印機,白熾燈,相機設備,然后按一下拍照開關,接着點擊打印,照片就出來了,最終關閉所有裝置。
各個設備要去單獨的打開,拍完照,又要單獨去關閉,顯然不是很方便。其實如果有一個開關能控制所有設備的開關,那么就能省下好多步驟,就可以大大的提高生產率了。
2.2外觀模式例子
其實這個就和我們的外觀模式相一致,我們可以定義為這套設備定義統一的接口,接口中的方法包括打開,拍照,打印,關閉,在使用外觀模式之前,直接在客戶端調用每個設備的開關,以及其他的操作,直接將客戶端和各個設備耦合在了一起。由於代碼比較簡單,在此不作演示,下面就只演示使用外觀模式的例子。

1 class Program 2 { 3 static void Main(string[] args) 4 { 5 6 ITakePicture takephoto = new TakePicture(); 7 takephoto.Open(); 8 takephoto.TakePictures(); 9 takephoto.Printing(); 10 takephoto.Close(); 11 Console.ReadKey(); 12 } 13 } 14 15 public class Computer 16 { 17 public void Open() 18 { 19 Console.WriteLine("電腦開了"); 20 } 21 public void Close() 22 { 23 Console.WriteLine("電腦已經關閉了"); 24 } 25 } 26 27 public class Light 28 { 29 public void Open() 30 { 31 Console.WriteLine("燈開了"); 32 } 33 public void Close() 34 { 35 Console.WriteLine("燈關閉了"); 36 } 37 } 38 39 public class Print 40 { 41 public void Open() 42 { 43 Console.WriteLine("打印機打開了"); 44 } 45 public void Printing() 46 { 47 Console.WriteLine("打印完成"); 48 } 49 public void Close() 50 { 51 Console.WriteLine("打印機已關閉"); 52 } 53 } 54 55 public class Cinema 56 { 57 public void Open() 58 { 59 Console.WriteLine("照相機打開了"); 60 } 61 public void Close() 62 { 63 Console.WriteLine("照相機已關閉"); 64 } 65 public void TakePictures() 66 { 67 Console.WriteLine("已經拍完了"); 68 } 69 } 70 71 public interface ITakePicture 72 { 73 void Open(); 74 void TakePictures(); 75 void Printing(); 76 void Close(); 77 } 78 79 public class TakePicture : ITakePicture 80 { 81 Computer computer = new Computer(); 82 Light light = new Light(); 83 Print print = new Print(); 84 Cinema cinema = new Cinema(); 85 public TakePicture() 86 { 87 88 } 89 public void Open() 90 { 91 light.Open(); 92 computer.Open(); 93 print.Open(); 94 cinema.Open(); 95 } 96 97 public void TakePictures() 98 { 99 cinema.TakePictures(); 100 } 101 102 public void Printing() 103 { 104 print.Printing(); 105 } 106 107 public void Close() 108 { 109 light.Close(); 110 computer.Close(); 111 print.Close(); 112 cinema.Close(); 113 } 114 }
可以看出外觀模式的好處是:簡化了設備的接口,同時降低了客戶端和各個設備的耦合。例如,照相機的照相方法改成了TakePhoto(),那么也只需要在外觀的實現方法中修改該設備的方法即可,不用去修改客戶端代碼。
2.3外觀模式需要注意的地方
外觀封裝了子設備的類,那么客戶端是如何調用子設備的方法的?
通過上面的代碼可以看出,對於每一個單獨的設備的方法的調用我們也是將設備的方法封裝到了外觀類中(如cinema的takepicture方法被封裝在外觀類的一個方法中),這里使用了一個原則:
最少知識原則:只和你的密友談話。
客戶端只和外觀談話,不和子設備,如相機,電腦等談話,降低了客戶端和設備的耦合度。
下面給出最少知識原則的指導思想:
就任何對象而言,在該對象的方法內,我們只應該調用屬於以下范圍的方法:
1.該對象本身
2.被當做方法的參數而傳過來的對象
3.該方法所創建或實例化的任何對象
4.對象的任何組件
盡管我們有時可以在客戶端使用takephoto.print.Printing();但是最好也不要使用,因為客戶端這樣不僅耦合了TakePhoto類還耦合了Print。所以盡可能自己封裝子設備的方法,以便減少客戶端和子設備的耦合。
三、適配器、外觀、裝飾者模式對比
在介紹裝飾者模式時,引出了一個開閉原則,即對修改關閉,對擴展開放。裝飾者模式主要強調的是在不改變原有類的基礎上,添加新功能。
適配器模式,主要是對適配對象進行調整,以便適合消費者的需求。從而達到消費者和被適配者解耦的目的。
外觀模式的特點主要是簡化接口,以及減少客戶端對外觀組件的耦合。因為如果客戶端變化來,組件的子系統變化了,不用影響客戶端。除此之外,在封裝組件時,適當的在外觀類中添加一些自己想要的規則。如上面例子中各設備的開關順序,或者拍照和打印之前其設備是否開啟等。
四、總結和源碼
本文主要通過舉例來介紹了適配器和外觀模式,最后對比了適配器,外觀,裝飾者模式的區別。