轉接器模式(Adapter Pattern)

  • 33203
  • 0

轉接器模式(Adapter Pattern)

以下程式碼範例來自於Head First Design Pattern一書!

 

轉接器故名思義,在真實世界中,我們常常會看到,像是三孔轉兩孔的電源插頭、不同國家電壓切換的旅行用轉接頭。

透過轉接器的轉換,就可以讓不同的裝置取得電力或是資訊!就像這樣!

image

那麼,在物件導向的轉接器是什麼呢?

假如我們有一套軟體系統,希望和其他廠商即有的程式庫搭配使用,但是這個新廠商所設計出來的介面,不同於舊廠商的介面。

image

這兩個介面無法匹配,所以無法運作!!

 

你一定不會想改變既有的程式碼來解決這個問題,特別是之前講的,當需求一變動,我們就要翻修一些我們既有的系統,這樣非常的不靈活。

而我們也無法改變廠商的程式碼。

 

轉接器類別就此誔生!我們可以寫一個類別,將新廠商介面轉接成我們所期盼的介面吧!

image 

轉接器實踐了我們所希望的介面,而且也能和廠商的介面溝通!

這個轉接器運作起來就是一個中介人,它將客戶發出的請求轉換成廠商類別所看的懂的請求!

image 

這個原理很淺顯易懂!

 

因此當你未來,看到一個含有包裝鏡頭盒的鏡頭

image

 

 

這個東西,看起來就是一隻高檔的長焦鏡頭,而且也有Canon的品牌標籤呢

image

 

 

該有的開關跟焦段刻度都有呢…

image 

那麼…他必定可能是一隻Canon的鏡頭具備鏡頭外觀介面轉接器的………………………杯子…

 

image

 

好了,回歸正題,同理可證,如果有一個東西朝你走過來,走起路來像一隻企鵝,叫起來像一隻企鵝,那麼他必定仍然可能只是一隻具備企鵝轉接器的…鴨子:

那麼這個名叫鴨子轉接器的轉接器到底是怎麼運作的呢?

這一次,企鵝實踐了以下介面,具備企鵝叫以及企鵝走路的能力了。

   1: public interface Penguins
   2: {
   3:     void Penguin_gobble();      //咯咯叫
   4:     void Penguin_walk();
   5: }

國王企鵝是企鵝的次類別!

 

   1: public class KingPenguins:Penguins   //國王企鵝
   2: {
   3:     public void Penguin_gobble()
   4:     {
   5:         Console.WriteLine("企鵝叫");
   6:     }
   7:  
   8:     public void Penguin_walk()
   9:     {
  10:         Console.WriteLine("企鵝走路"); 
  11:     }
  12: }

而鴨子的介面呢?

   1: public interface Duck
   2: {
   3:     void Duck_quack();        //嗄嗄叫
   4:     void Duck_walk();
   5: }

綠頭鴨是鴨子的介面的一個具象實踐!

   1: public class MallardDuck:Duck    //綠頭鴨
   2: {
   3:     public void Duck_quack()
   4:     {
   5:         Console.WriteLine("鴨子叫");
   6:     }
   7:  
   8:     public void Duck_walk()
   9:     {
  10:         Console.WriteLine("鴨子走路"); 
  11:     }
  12: }

 

因此,假如我們目前動物園中因為企鵝生病了,為了讓遊客看到企鵝,園方想用一些鴨子物件來充數的話,因為介面設計的不同,所以不能公然拿來用…。

那麼只好寫一個轉接器了:

   1: public class DuckAdapter : Penguins
   2: {
   3:     Duck gDuck;
   4:  
   5:     public DuckAdapter(Duck pDuck)
   6:     {
   7:         this.gDuck = pDuck;
   8:     }
   9:  
  10:     #region Penguins 成員
  11:     public void Penguin_gobble()
  12:     {
  13:         gDuck.Duck_quack();                                  //因為都是叫,所以直接調用Duck中的方法,就很簡單了!
  14:         Console.Write("壓低聲音!\n");   //
  15:     }
  16:  
  17:     public void Penguin_walk()
  18:     {
  19:         gDuck.Duck_walk();
  20:         Console.Write("翅膀向下伸值,左右搖擺!\n");   //
  21:     }
  22:  
  23:     #endregion
  24: }

這個轉接器我們要先實踐我們希望轉換成的類別,我們是動物園的企鵝期望被看到,所以我們是實現一個鴨子轉接器,來轉換成企鵝的樣子!

在當中的建構式中,我們要取得被轉換者的參考,也就是取得要充數成企鵝的鴨子。

 

而明確實作企鵝類別中,我們發現要像企鵝的話,鴨子的叫聲要壓低聲音,然後走路的樣子要模仿企鵝搖擺搖擺!

最後我們只需要一些程式碼來測試我們的轉接器

   1: static void Main(string[] args)
   2:  {
   3:      //先看看一隻正版的企鵝
   4:      KingPenguins penguin = new KingPenguins();
   5:      MallardDuck duck = new MallardDuck();     //要充數成企鵝的鴨子!
   6:    
   7:      Penguins duckAdapter = new DuckAdapter(duck);              //叫鴨子穿上企鵝套裝(轉接器)
   8:      Console.Write("\n\n鴨子展示:\n");                                                 //先看看正版鴨子
   9:      duck.Duck_quack();
  10:      duck.Duck_walk();
  11:  
  12:      //我們以下建立了一個企鵝展示台
  13:      Console.WriteLine("\n\n國王企鵝展示:");
  14:      testPenguin(penguin);
  15:  
  16:      Console.WriteLine("\n\n假裝是企鵝的鴨子展示:");
  17:      testPenguin(duckAdapter);
  18:  
  19:      Console.ReadKey();
  20:  }

上述測試程式的重點就是,我們透過鴨子轉接器來假裝成企鵝的樣子,而我們實作了一個TestPenguin的方法,我們傳入了宣告為penguin的鴨子轉接器的物件。

鴨子轉接器中則實作了要表現的像企鵝一樣的方法。

測試的結果如下:

image

 

 

現在我們已經知道什麼是轉接器了!

其轉接器的模式如下:

image

這個模式可以讓客戶和被轉接者之間是鬆綁的,他們並不認識彼此,而客戶接收到呼叫的結果的時候,並未察覺這一切是透過一個轉接器中介傳導。

這個轉接器的方法,似乎也是省不了多少程式碼的撰寫!不過,幸好的是,相形之下,這個轉接器模式提供了一個轉接器類別,將所有的改變封裝在一個類別中。

比起更改客戶端的程式來呼叫新的介面,這個模式是比較好的做法!

 

真實世界中的轉接器模式的定義如下:

將一個類別的介面,轉換成另一個介面以供客戶使用。轉接器讓原本介面不相容的類別可以合作。

 

類別圖如下:

image

 

HeadFirst Design Pattern後記:

轉接器分成了”物件轉接器”以及”類別轉接器”,上圖是物件轉接器,若要實踐類別轉接器的話,就需要多重繼承,然而在C#跟Java中都不支援

若後續在多重繼承的語言中,仍然有可能會實作到(且看下一篇)。另一個問題是轉接器只能封裝一個類別嗎?不見得,範例總是最單純的環境狀況,在下下一篇

我們會看到一些情況,需要讓一個轉接器包裝多個被轉接者。也就會涉及下一個設計模式:表象模式(Facade Pattern),就靜待分曉吧。

最後這個轉接器模式充滿著良好的00設計守則:透過使用物件合成,以修改的介面包裝被轉接者,這種做法可以達到讓被轉接者的任何次類別,都可以搭配使用轉接器。最後提醒這個模式是如何讓客戶和介面之間建立關係,而不是讓客戶和實踐的內容建立關係。我們可以使用數個轉接器,每個一個都負責轉換不同組的終端類別。

或是像本例中,加入新的實踐內容,只要遵守目標介面就可以囉。