設計模式解密(5)- 外觀模式(門面模式)
1、簡介
外觀模式提供了一個統一的接口,用來訪問子系統中的一群接口。外觀定義了一個高層接口,讓子系統更容易使用。
使用外觀模式時,我們創建了一個統一的類,用來包裝子系統中一個或多個復雜的類,客戶端可以直接通過外觀類來調用內部子系統中方法,從而外觀模式讓客戶和子系統之間避免了緊耦合。
外觀模式的目的不是為了給子系統添加新的功能接口,而是為了讓外部減少與子系統內多個模塊的交互,松散耦合,從而讓外部能夠更簡單地使用子系統。
外觀模式的本質是:封裝交互,簡化調用。
英文:Facade
類型:結構型模式
2、實例引入
背景:模仿安裝智能家居前後的對比
操作燈光接口
package com.designpattern.facade; /** * 類說明 :燈光接口 */ public interface LightI { /** * 開燈 */ public void on(); /** * 關燈 */ public void off(); }
package com.designpattern.facade.impl; import com.designpattern.facade.LightI; /** * 類說明 :燈光接口實現類 */ public class Light implements LightI { @Override public void on() { System.out.println("打開燈"); } @Override public void off() { System.out.println("關閉燈"); } }
操作電視接口
package com.designpattern.facade; /** * 類說明 :電視接口 */ public interface TVI { /** * 開機 */ public void on(); /** * 關機 */ public void off(); }
package com.designpattern.facade.impl; import com.designpattern.facade.TVI; /** * 類說明 :電視接口實現類 */ public class TV implements TVI { @Override public void on() { System.out.println("打開電視"); } @Override public void off() { System.out.println("關閉電視"); } }
在安裝智能家居前:
package com.designpattern.facade; import com.designpattern.facade.impl.Light; import com.designpattern.facade.impl.TV; /** * 類說明 :測試 */ public class Test { public static void main(String[] args) { //----晚上下班回到家---- //開燈光 LightI light = new Light(); light.on(); //看電視 TVI tv = new TV(); tv.on(); //-----睡覺----- //關燈光 light.off(); //關電視 tv.off(); } }
結果:
打開燈 打開電視 關閉燈 關閉電視
我們需要一個一個把需要開的電器打開,是不是好麻煩?
下面咱們引入智能家居
package com.designpattern.facade; /** * 類說明 :智能家居總開關接口 */ public interface SmartHomeI { /** * 總開 */ public void on(); /** * 總關 */ public void off(); }
package com.designpattern.facade.impl; import com.designpattern.facade.LightI; import com.designpattern.facade.SmartHomeI; import com.designpattern.facade.TVI; /** * 類說明 :智能家居總開關接口 */ public class SmartHome implements SmartHomeI { private LightI light = new Light(); private TVI tv = new TV(); @Override public void on() { light.on(); tv.on(); } @Override public void off() { light.off(); tv.off(); } }
安裝智能家居以後:
package com.designpattern.facade; import com.designpattern.facade.impl.Light; import com.designpattern.facade.impl.SmartHome; import com.designpattern.facade.impl.TV; /** * 類說明 :測試 */ public class Test { public static void main(String[] args) { SmartHomeI smarthome = new SmartHome(); //----晚上下班回到家---- smarthome.on(); //-----睡覺----- smarthome.off(); } }
結果:
打開燈 打開電視 關閉燈 關閉電視
就一個開關是不是很方便?
3、解決的問題
從上面的例子看:
外觀模式對客戶屏蔽了子系統組件,從而簡化了接口,減少了客戶處理的對象數目並使子系統的使用更加簡單。
外觀模式實現了子系統與客戶之間的松耦合關系,而子系統內部的功能組件是緊耦合的,松耦合使得子系統的組件變化不會影響到它的客戶。(比如將來我換一臺電視,只需要修改一下電視接口即可,對外部的智能家居接口不需要修改,從而不影響外部使用者)
4、應用場景
需要將設計進行分層時考慮外觀模式;(在層次化結構中,可以使用外觀模式定義系統中每一層的入口,其中三層架構就是這樣的一個例子)
在開發階段,子系統往往因為重構變得越來越復雜,增加外觀模式可以提供一個簡單的接口,減少它們之間的依賴;(外一個復雜的子系統提供一個簡單的接口)
在維護一個遺留的大型系統時,可以這個系統已經非常難以維護和擴展,可以為新系統開發一個外觀類,來提供設計粗糙或高度復雜的遺留代碼的比較清晰簡單的接口,讓新系統與外觀類交互,外觀模式與遺留代碼交互所有復雜的工作;(提供子系統的獨立性)
5、優缺點
優點:
外觀模式降低了客戶端對子系統使用的復雜性;
外觀模式松散了客戶端與子系統的耦合關系,讓子系統內部的模塊能更容易擴展和維護;
通過合理使用外觀模式,可以幫助我們更好的劃分訪問的層次;
使用外觀模式還有一個附帶的好處,就是能夠有選擇性地暴露方法。一個模塊中定義的方法可以分成兩部分,一部分是給子系統外部使用的,一部分是子系統內部模塊之間相互調用時使用的。有了外觀類,那麽用於子系統內部模塊之間相互調用的方法就不用暴露給子系統外部了;
缺點:
如果增加新的子系統可能需要修改外觀類或客戶端的源代碼,這樣就違背了”開——閉原則“(不過這點也是不可避免);
6、與其他模式對比
外觀模式與適配器模式非常類似;
外觀模式與適配器模式不同的是:適配器模式是將一個對象包裝起來以改變其接口,而外觀是將一群對象 ”包裝“起來以簡化其接口。它們的意圖是不一樣的,適配器是將接口轉換為不同接口,而外觀模式是提供一個統一的接口來簡化接口;
外觀模式旨在:子系統的高層接口;
適配器模式旨在:對象接口的變化;
7、總結
外觀模式,為子系統的一組接口提供一個統一的接口,該模式定義了一個高層接口,這一個高層接口使的子系統更加容易使用。並且外觀模式可以解決層結構分離、降低系統耦合度和為新舊系統交互提供接口功能。
PS:源碼地址 https://github.com/JsonShare/DesignPattern/tree/master
設計模式解密(5)- 外觀模式(門面模式)