設計模式(二):適配器模式和外觀模式
適配器模式
一、出國必備適配器
隨著中國經濟的快速發展,我們也越來越有錢了。出國旅遊也不是什麽新鮮事了。泰國是中國人最愛去的旅遊地之一。當我們程序員去泰國旅遊時,可能會帶著筆記本電腦去,以便隨時可以工作(不要問我為什麽)。如果你帶著你的惠普電腦去泰國遊玩,你會發現泰國酒店的插座一般是這樣的:。而你的電腦插頭卻是這樣的:。如果你要充電是不是尷尬了?所以在你去之前你要帶上一個插座的適配器(轉換插頭):。有了這個適配器你的電腦就可以充電了。現在我們將這個例子用代碼展現出來。
1.首先我們有一個惠普電腦HpComputer,我們電腦充電需要的一個中國的插座接口ChinaOutlet
/** * 惠普電腦 *@author wuqi * @Date 2019/2/11 11:22 */ public class HpComputer { public void charge(ChinaOutlet chinaOutlet){ chinaOutlet.work(); } }
/** * 中國的插座 * @author wuqi * @Date 2019/2/11 11:33 */ public interface ChinaOutlet { void work(); }
2.但是當我們來到泰國時,卻發現泰國的插座接口TailandOutlet並不適用於我們的電腦
/** * 泰國的插座 * @author wuqi * @Date 2019/2/11 11:45 */ public interface TailandOutlet { void work(); }
/** * 泰國酒店的插座 * @author wuqi * @Date 2019/2/11 11:42 */ public class HotelTailandOutlet implements TailandOutlet { @Override public void work() { System.out.println("HotelTailandOutlet work,charging..."); } }
3.所以我們需要一個適配器來適配泰國的插座接口,以便我們的電腦可以使用。讓我們的適配器實現中國插座接口,並利用組合的方式將泰國的插座接口封裝進來給我們的電腦提供充電的功能
/** * 泰國插座適配器 * @author wuqi * @Date 2019/2/11 13:47 */ public class TailandOutletAdapter implements ChinaOutlet { TailandOutlet tailandOutlet; public TailandOutletAdapter(TailandOutlet tailandOutlet){ this.tailandOutlet = tailandOutlet; } @Override public void work() { tailandOutlet.work(); } }
4.最後我們測試以下,可以看到,我們的電腦已經可以正常充電了
/** * 適配器模式測試 * @author wuqi * @Date 2019/2/11 13:49 */ public class AdapterPatternTest { public static void main(String[] args) { //泰國的插座 HotelTailandOutlet hotelTailandOutlet = new HotelTailandOutlet(); //泰國插座的適配器 ChinaOutlet chinaOutlet = new TailandOutletAdapter(hotelTailandOutlet); HpComputer hpComputer = new HpComputer(); hpComputer.charge(chinaOutlet); } }
二、定義適配器模式
上面的例子即是適配器模式,下面我們給適配器模式下一個定義
1.概念
將一個類的接口,轉換成客戶期望的另一個接口。適配器讓原本不兼容的類可以合作無間。
2.概念解析
結合上面插座的例子,我們很好理解適配器模式的概念,如果有一個接口提供了我們需要的功能(泰國插座),但是我們現有的接口和該接口又相沖突(中國插座),我們就可以定義一個適配器(TailandOutletAdapter)來適配該接口,以此來兼容我們現有的接口。
3.UML類圖
三、適配器模式的應用場景
適配器模式的定義很直觀的反映出了適配器模式的應用場景,它適用於將一個接口轉換成你所需要的另一個接口的場景。
四、適配器模式的優缺點
優點:適配器將原本不可使用的接口轉換成了另一個接口,這可以讓客戶從實現的接口解耦。如果在一段時間之後,我們想要改變接口,適配器可以將改變的部門包裝起來,客戶不必為了應對不同的接口而每次跟著修改。
缺點:1.如果適配接口方法很多,那麽適配器則需要一個一個去適配,這樣做就會比較麻煩。上面插座的例子中,我們需要適配的方法只有一個work(),我們適配的工作量就很小,但是如果還有額外的幾十甚至幾百個方法時,那麽我們還要去把這些額外的方法一個個給適配掉,毫無疑問,這是非常費時費力的。
2.如果在適配接口中有一個方法,但是被適配的接口卻沒有,那麽目標方法將無法適配,這時則需要我們額外的進行處理。比如,如果我們要將Enumeration適配成Iterator,那麽我們就需要適配Iterator的remove方法,但是Enumeration中並沒有該功能,那麽該功能就會適配失敗,此時就需要我們對該方法進行額外處理。
外觀模式
一、用外觀模式改善生活
我們程序員的生活都很累,難得到周末,我們一般很少外出,基本都是窩在家裏看電視(其實是看手機,假設我們窩在家裏看電視)。假設現在是周日的中午,你吃過午飯,感覺很疲憊,這時你就想躺在家裏舒舒服服的看個電視劇。而我們想舒舒服服的看個電視劇,我們不僅僅要打開電視,我們可能還要打開屋裏的電燈,溫度過高或過低我們還要打開空調,為了保證私密性,還要關上我們的電窗,最後,我們就可以打開電視做下舒舒服服的看電視劇了。下面我們用代碼來展現出這個過程。
1.首先是我們需要操作的電器
/** * 電燈 * * @author wuqi * @Date 2019/2/11 14:48 */ public class Light { public void open(){ System.out.println("Light open..,"); } public void close(){ System.out.println("Light close..."); } }
/** * 空調 * @author wuqi * @Date 2019/2/11 14:45 */ public class AirCondition { public void open(){ System.out.println("AirCondition open"); } public void close(){ System.out.println("AirCondition close..."); } }
/** * 電窗 * @author wuqi * @Date 2019/2/11 14:47 */ public class PowerWindow { public void open(){ System.out.println("PowerWinodw open..,"); } public void close(){ System.out.println("PowerWindow close..."); } }
/** * 電視機 * @author wuqi * @Date 2019/2/11 14:44 */ public class TV { public void open(){ System.out.println("TV open..."); } public void close(){ System.out.println("TV closed..."); } }
2.最後我們進行觀看電視劇
/** * 不使用外觀模式觀看電視劇 * @author wuqi * @Date 2019/2/12 10:04 */ public class SimpleTest { public static void main(String[] args) { Light light = new Light(); AirCondition airCondition = new AirCondition(); PowerWindow powerWindow = new PowerWindow(); TV tv = new TV(); light.open(); airCondition.open(); powerWindow.close(); tv.open(); } }
運行可以看到我們可以看電視劇了
但是,你會發現我們就是看個電視劇,卻要一個個的去打開或關閉各個電器,麻煩的是如果我們看完電視劇後,我們還要對這些電器又重復操作一遍,我們現在只有四個電器還好點,如果我們有十幾個電器需要操作,那真的就很麻煩了。
這時候外觀模式就派上用場了,想一下,我們可以定義一個觀看電視劇的外觀,這個外觀假設就是一個遙控器,遙控器上有兩個按鈕,一個觀看電視的按鈕,一個關閉電視的按鈕。當我們按下觀看電視的按鈕時,不僅會打開電視還會將電燈打開,空調打開,關上電窗。關閉按鈕按下時,就會起到反效果,這樣我們看電視劇就會變得非常的簡單美好。下面我們就來用外觀模式來改善我們觀看電視的過程。
1.定義我們的外觀——遙控器
/** * 遙控器。將各個電器的操作整合在一起,充當了外觀的角色。 * @author wuqi * @Date 2019/2/11 14:50 */ public class RemoteControlFacade { private Light light; private AirCondition airCondition; private PowerWindow powerWindow; private TV tv; public RemoteControlFacade(Light light, AirCondition airCondition, PowerWindow powerWindow, TV tv){ this.light = light; this.airCondition = airCondition; this.powerWindow = powerWindow; this.tv = tv; } /** * 看電視 */ public void pushOpenTvButton(){ light.open(); airCondition.open(); powerWindow.close(); tv.open(); } /** *關閉電視 */ public void pushCloseTvButton(){ light.close(); airCondition.close(); powerWindow.open(); tv.close(); } }
2.使用外觀觀看電視劇
/** * 外觀模式測試 * @author wuqi * @Date 2019/2/11 15:01 */ public class FacadePatternTest { public static void main(String[] args) { Light light = new Light(); AirCondition airCondition = new AirCondition(); PowerWindow powerWindow = new PowerWindow(); TV tv = new TV(); //創建觀看電視的外觀類——遙控器 RemoteControlFacade remoteControlFacade = new RemoteControlFacade(light,airCondition,powerWindow,tv); //觀看電視 remoteControlFacade.pushOpenTvButton(); //結束觀看 remoteControlFacade.pushCloseTvButton(); } }
運行結果:
可以看到使用外觀模式將我們觀看電視的的操作變得十分的簡單,十分的美好。
二、定義外觀模式
1.概念
外觀模式(Facade Pattern)它將一個或數個的類的復雜的一切都隱藏在背後,只顯露出一個幹凈美好的外觀。也就是簡化了接口。
2.概念解析
對應於我們觀看電視的例子我們來解析下這個概念。我們一開始觀看電視時,是一個個的將電器打開,看完電視後,又將這些電器一個個關閉。後來將這些電器都封裝在一個外觀(遙控器)中提供出一個觀看電視方法,一個關閉電視方法。而觀看電視和關閉電視的方法其實就是調的各個電器的方法打開或關閉方法。遙控器就是將這些電器的操作隱藏在背後,只露出觀看電視和關閉電視這個美好的外觀。
3.UML類圖
3.外觀模式中的OO設計原則
1.最少知識原則
外觀模式中遵守了一個OO的設計原則:最少知識原則(又叫做墨忒耳法則,有的網上叫做迪米特法則),只和你的密友交談。
最少知識原則要求我們在設計一個系統時,不管是任何對象,你都要註意它所交互的類有哪些,並註意它和這些類是如何交互的。這個原則希望我們在設計中,不要讓太多的類耦合在一起,免得修改其中的一部分,會影響到其他的部分。如果許多類之間相互依賴,那麽這個系統就會變成一個易碎的系統,它需要花費許多維護成本,也會因為太復雜而不容易被他人了解。
很顯然,我們的外觀模式將復雜的子系統隱藏起來,只提供一個接口,減少了多個類之間的耦合,符合這個原則。
2.如何減少類之間的依賴
怎麽樣才能減少類之間的依賴,最少知識原則提供了一些方針,就任何對象而言,在該對象的方法內,我們只應該調用屬於以下範圍的方法:
- 該對象本身
- 被當作方法的參數而傳進來的對象
- 此方法所創建或實例化的對象
- 對象的任何組件
下面通過一個代碼的例子來說明:
三、外觀模式的應用場景
外觀模式適用於將復雜的子系統隱藏一起來,並對外提供一個簡單外觀,簡化接口的場景
四、外觀模式的優缺點
1.優點:外觀模式不僅將復雜的子系統隱藏起來,並提供簡單的外觀,簡化了接口。它也將客戶端和復雜的子系統解耦
2.缺點:因為外觀模式需要將復雜的子系統封裝隱藏起來,所以會產生較多的小類,會增加系統的體積。
適配器模式和外觀模式註意點
雖然大多數教科書所采用的例子中適配器只適配一個類,但是你可以選擇適配許多類來提供一個接口讓客戶編碼。類似的,一個外觀也可以只針對一個擁有復雜接口的類來提供簡化接口。兩種模式的差異,不在於它們“包裝了”幾個類,而在於它們的意圖。適配器的意圖是,“改變”接口符合客戶的期望;而外觀模式的意圖是,提供子系統的簡化接口。
設計模式(二):適配器模式和外觀模式