結構模式(Structural Pattern)描述如何將類或者對象結合在一塊兒造成更大的結構。結構模式描述兩種不一樣的東西:類與類的實例。根據這一點,結構模式能夠分爲類的結構模式和對象的結構模式。app
後續內容將包括如下結構模式:ide
適配器模式把一個類的接口變換成客戶端所期待的另外一種接口,從而使本來接口不匹配而沒法在一塊兒工做的兩個類可以在一塊兒工做。this
名稱由來spa
這很像變壓器(Adapter),變壓器把一種電壓變換成另外一種電壓。美國的生活用電電壓是110V,而中國的電壓是220V。若是要在中國使用美國電器,就必須有一個能把220V電壓轉換成110V電壓的變壓器。這個變壓器就是一個Adapter。設計
Adapter模式也很像貨物的包裝過程:被包裝的貨物的真實樣子被包裝所掩蓋和改變,所以有人把這種模式叫作包裝(Wrapper)模式。事實上,你們常常寫不少這樣的Wrapper類,把已有的一些類包裝起來,使之有能知足須要的接口。代理
適配器模式的兩種形式code
適配器模式有類的適配器模式和對象的適配器模式兩種。咱們將分別討論這兩種Adapter模式。orm
由圖中能夠看出,Adaptee類沒有Request方法,而客戶期待這個方法。爲了使客戶可以使用Adaptee類,提供一箇中間環節,即類Adapter類,Adapter類實現了Target接口,並繼承自Adaptee,Adapter類的Request方法從新封裝了Adaptee的SpecificRequest方法,實現了適配的目的。對象
由於Adapter與Adaptee是繼承的關係,因此這決定了這個適配器模式是類的。blog
該適配器模式所涉及的角色包括:
目標(Target)角色:這是客戶所期待的接口。由於C#不支持多繼承,因此Target必須是接口,不能夠是類。
源(Adaptee)角色:須要適配的類。
適配器(Adapter)角色:把源接口轉換成目標接口。這一角色必須是類。
下面的程序給出了一個類的Adapter模式的示意性的實現:
// Class Adapter pattern -- Structural example using System; // "ITarget" interface ITarget { // Methods void Request(); } // "Adaptee" class Adaptee { // Methods public void SpecificRequest() { Console.WriteLine("Called SpecificRequest()" ); } } // "Adapter" class Adapter : Adaptee, ITarget { // Implements ITarget interface public void Request() { // Possibly do some data manipulation // and then call SpecificRequest this.SpecificRequest(); } } /// <summary> /// Client test /// </summary> public class Client { public static void Main(string[] args) { // Create adapter and place a request ITarget t = new Adapter(); t.Request(); } }
從圖中能夠看出:客戶端須要調用Request方法,而Adaptee沒有該方法,爲了使客戶端可以使用Adaptee類,須要提供一個包裝(Wrapper)類Adapter。這個包裝類包裝了一個Adaptee的實例,從而將客戶端與Adaptee銜接起來。因爲Adapter與Adaptee是委派關係,這決定了這個適配器模式是對象的。
該適配器模式所涉及的角色包括:
目標(Target)角色:這是客戶所期待的接口。目標能夠是具體的或抽象的類,也能夠是接口。
源(Adaptee)角色:須要適配的類。
適配器(Adapter)角色:經過在內部包裝(Wrap)一個Adaptee對象,把源接口轉換成目標接口。
下面的程序給出了一個類的Adapter模式的示意性的實現:
// Adapter pattern -- Structural example using System; // "Target" class Target { // Methods virtual public void Request() { // Normal implementation goes here } } // "Adapter" class Adapter : Target { // Fields private Adaptee adaptee = new Adaptee(); // Methods override public void Request() { // Possibly do some data manipulation // and then call SpecificRequest adaptee.SpecificRequest(); } } // "Adaptee" class Adaptee { // Methods public void SpecificRequest() { Console.WriteLine("Called SpecificRequest()" ); } } /// <summary> /// Client test /// </summary> public class Client { public static void Main(string[] args) { // Create adapter and place a request Target t = new Adapter(); t.Request(); } }
在如下各類狀況下使用適配器模式:
一、 系統須要使用現有的類,而此類的接口不符合系統的須要。
二、 想要創建一個能夠重複使用的類,用於與一些彼此之間沒有太大關聯的一些類,包括一些可能在未來引進的類一塊兒工做。這些源類不必定有很複雜的接口。
三、 (對對象適配器而言)在設計裏,須要改變多個已有子類的接口,若是使用類的適配器模式,就要針對每個子類作一個適配器,而這不太實際。
下面的程序演示了Class Adapter與Object Adapter的應用。
// Example of implementing the Adapter pattern using System; // Target public interface ICar { void Drive(); } // Direct use without Adapter public class CToyota : ICar { public void Drive() { Console.WriteLine("Vroom Vroom, we're off in our Toyota"); } } // Adaptee public class CCessna { public void Fly() { Console.WriteLine("Static runup OK, we're off in our C172"); } } // Class Adapter public class CDrivableCessna : CCessna, ICar { public void Drive() { base.Fly(); } } // Object Adapter public class CDrivableCessna2 : ICar { private CCessna m_oContained; public CDrivableCessna2() { m_oContained = new CCessna(); } public void Drive() { m_oContained.Fly(); } } // Client public class Client { public static void Main(string[] args) { ICar oCar = new CToyota(); Console.Write("Class Adapter: Driving an Automobile"); oCar.Drive(); oCar = new CDrivableCessna(); Console.Write("Driving a Cessna"); oCar.Drive(); oCar = new CDrivableCessna2(); Console.Write(" Object Adapter: Driving a Cessna"); oCar.Drive(); } }
Adapter模式在實現時有如下這些值得注意的地方:
一、 目標接口能夠省略,模式發生退化。但這種作法看似平庸而並不平庸,它可使Adaptee沒必要實現不須要的方法(能夠參考Default Adapter模式)。其表現形式就是父類實現缺省方法,而子類只需實現本身獨特的方法。這有些像模板(Template)模式。 二、 適配器類能夠是抽象類。 三、 帶參數的適配器模式。使用這種辦法,適配器類能夠根據參數返還一個合適的實例給客戶端。