如果你需要在歐洲國家使用美國制造的筆記本電腦,你可能需要使用一個交流電的適配器。你知道適配器的作用:它位于美式插頭和歐式插座的中間,它的工作是將歐式插座轉換成美式插座,好讓美式插頭可以插進這個插座得到電力。或者也可以這么認為:適配器改變了插座的接口,以符合美式筆記本電腦的需求。好了,這是真實世界的適配器,那面向對象適配器又是什么呢?其實,OO適配器和真實世界的適配器扮演著同樣的角色:將一個接口轉換成另一個接口,以符合客戶的期望。
假設已有一個軟件系統,你希望它能和一個新的產商類庫搭配使用,但是這個新產商所設計出來的接口,不同于舊產商的接口:
你不想改變現有的代碼,解決這個問題(而且你也不能改變產商的代碼)。所以該怎么做?你可以寫一個類,將新產商接口轉換成你所期望的接口。
這個適配器工作起來就如同一個中間人,它將客戶所發出的請求轉換成產商類能理解的請求。這個適配器實現了你的類所期望的接口,而且這個適配器也能和產商的接口溝通。
火雞轉換器
我們先來看看一個簡化的鴨子和火雞的接口和類:
// 鴨子基類public interface Duck { public void quack(); public void fly();} // 綠頭鴨是鴨子的子類,實現了鴨子的呱呱叫和飛行的能力public class MallardDuck implements Duck{ @Override public void quack() { System.out.PRintln("Quack"); } @Override public void fly() { System.out.println("I'm flying"); }} // 火雞基類public interface Turkey { // 火雞不會呱呱叫,只會咯咯叫 public void gobble(); // 火雞會飛,雖然飛不遠 public void fly();} // 野生火雞public class WildTurkey implements Turkey{ @Override public void gobble() { System.out.println("Gobble gobble"); } @Override public void fly() { System.out.println("I'm flying a short distance"); }}現在,假設你缺鴨子對象,想用一些火雞對象來冒充。顯而易見,因為火雞的接口不同,所以我們不能公然拿來用。那么,寫個適配器吧:
// 首先,你需要實現想轉換成的類型接口,也就是你的客戶期望看到的接口public class TurkeyAdapter implements Duck { Turkey turkey; // 接著,需要取得要適配的對象引用,這里我們引用構造器取得這個引用 public TurkeyAdapter(Turkey turkey) { this.turkey = turkey; } // 現在我們需要實現接口中所有的方法。quack()在類之間的轉換很簡單, // 只要調用gobble()接可以了 @Override public void quack() { turkey.gobble(); } // 固然兩個接口都具備了fly()方法,火雞的飛行距離很短,不像鴨子可以長途飛行。 // 要讓鴨子的飛行和火雞的飛行能夠對應,必須連續五次調用火雞的fly()來完成 @Override public void fly() { for (int i = 0; i < 5; i++) { turkey.fly(); } }}適配器模式解析
客戶使用適配器的過程如下:1. 客戶通過目標接口調用適配器的方法對適配器發出請求;2. 適配器使用被適配者接口把請求轉換成被適配者的一個或多個調用接口;3. 客戶接收到調用的結果,但并未察覺這一切是適配器在起轉換作用。
請注意,客戶和被適配者是解耦的,一個不知道另一個。
現在,我們知道,這個模式可以通過創建適配器進行接口轉換,讓不兼容的接口變成兼容。這可以讓客戶從實現的接口解耦。如果在一段時間之后,我們想要改變接口,適配器可以將改變的部分封裝起來,客戶就不必為了應對不同的接口而每次跟著修改。
這個適配器模式充滿著良好的OO設計原則:使用對象組合,以修改的接口包裝被適配者。這種做法還有額外的優點,那就是,被適配者的任何子類,都可以搭配著適配器使用。
也請留意,這個模式是如何把客戶和接口綁定起來,而不是和實現綁定起來的。我們可以使用數個適配器,每一個都負責轉換不同組的后臺類。或者,也可以加上新的實現,只要它們遵守目標接口就可以。
原文出處: cashow
新聞熱點
疑難解答