第一:情境類比
比如說我要買電腦
第一種辦法就是,完全靠自己,自己去買配件,然後自己組裝。這個辦法要求自己對配件比較熟悉。
第二種辦法就是,找一家專業的裝機公司,提出自己的要求, 然後等著拿電腦就行了。
很明顯,第二種辦法雖然多花錢,可是客戶不需要瞭解那麼多。
通過這個情境,我們可以將第二種辦法抽象為如,也就是我們的面板模式。
第二:問題由來
範例程式碼如下
package demo02.facade;/** * 示意配置描述的資料Model,真實的配置資料會很多 */public class ConfigModel {/** * 是否需要產生表現層,預設是true */private boolean needGenPresentation = true;/** * 是否需要產生邏輯層,預設是true */private boolean needGenBusiness = true;/** * 是否需要產生DAO,預設是true */private boolean needGenDAO = true;public boolean isNeedGenPresentation() {return needGenPresentation;}public void setNeedGenPresentation(boolean needGenPresentation) {this.needGenPresentation = needGenPresentation;}public boolean isNeedGenBusiness() {return needGenBusiness;}public void setNeedGenBusiness(boolean needGenBusiness) {this.needGenBusiness = needGenBusiness;}public boolean isNeedGenDAO() {return needGenDAO;}public void setNeedGenDAO(boolean needGenDAO) {this.needGenDAO = needGenDAO;}}
package demo02.facade;/** * 示意組態管理,就是負責讀取設定檔, 並把設定檔的內容設定到配置Model中去,是個單例 */public class ConfigManager {private static ConfigManager manager = null;private static ConfigModel cm = null;private ConfigManager() {}public static ConfigManager getInstance() {if (manager == null) {manager = new ConfigManager();cm = new ConfigModel();// 讀取設定檔,把值設定到ConfigModel中去}return manager;}/** * 擷取配置的資料 * * @return 配置的資料 */public ConfigModel getConfigData() {return cm;}}
package demo02.facade;/** * 示意產生表現層的模組 */public class Presentation {public void generate() {// 1:從組態管理裡面擷取相應的配置資訊ConfigModel cm = ConfigManager.getInstance().getConfigData();if (cm.isNeedGenPresentation()) {// 2:按照要求去產生相應的代碼,並儲存成檔案System.out.println("正在產生表現層代碼檔案");}}}
package demo02.facade;/** * 示意產生邏輯層的模組 */public class Business {public void generate() {// 1:從組態管理裡面擷取相應的配置資訊ConfigModel cm = ConfigManager.getInstance().getConfigData();if (cm.isNeedGenBusiness()) {// 2:按照要求去產生相應的代碼,並儲存成檔案System.out.println("正在產生邏輯層代碼檔案");}}}
package demo02.facade;/** * 示意產生資料層的模組 */public class DAO {public void generate() {// 1:從組態管理裡面擷取相應的配置資訊ConfigModel cm = ConfigManager.getInstance().getConfigData();if (cm.isNeedGenDAO()) {// 2:按照要求去產生相應的代碼,並儲存成檔案System.out.println("正在產生資料層代碼檔案");}}}
package demo02.facade;public class Client {public static void main(String[] args) {// 現在沒有設定檔,就直接使用預設的配置// 通常情況下,三層都應該產生,也就是說用戶端必須// 對這些模組都有瞭解,才能夠正確使用它們new Presentation().generate();new Business().generate();new DAO().generate();}}
通常情況下,三層都應該產生,也就是說用戶端必須對這些模組都有瞭解,才能夠正確使用它們,這無疑是個大麻煩,因為用戶端不能簡單的使用代碼,如果某個功能模組發生了變化,還要引起用戶端發生變化。
第三:解決辦法
這裡就需要我們的裝機公司了,也就是今天的主題,面板模式。
面板模式定義:為子系統的一組介面提供一個一致的介面,Façade模式定了一個高層介面,這個介面使得子系統更加容易使用。如
舉一個例子吧,先把圖貼出來,然後再貼代碼
package demo02.facade01;/** * A模組的介面 */public interface AModuleApi {/** * 示意方法,A模組對外的一個功能方法 */public void testA();}
package demo02.facade01;public class AModuleImpl implements AModuleApi {public void testA() {System.out.println("現在在A模組裡面操作testA方法");}}
package demo02.facade01;public interface BModuleApi {public void testB();}
package demo02.facade01;public class BModuleImpl implements BModuleApi {public void testB() {System.out.println("現在在B模組裡面操作testB方法");}}
package demo02.facade01;public interface CModuleApi {public void testC();}
package demo02.facade01;public class CModuleImpl implements CModuleApi {public void testC() {System.out.println("現在在C模組裡面操作testC方法");}}
package demo02.facade01;/** * 外觀對象 */public class Facade {/** * 示意方法,滿足客戶需要的功能 */public void test() {// 在內部實現的時候,可能會調用到內部的多個模組AModuleApi a = new AModuleImpl();a.testA();BModuleApi b = new BModuleImpl();b.testB();CModuleApi c = new CModuleImpl();c.testC();}}
package demo02.facade01;public class Client {public static void main(String[] args) {// 不用Facade,需要自己跟多個模組互動//AModuleApi a = new AModuleImpl();//a.testA();//BModuleApi b = new BModuleImpl();//b.testB();//CModuleApi c = new CModuleImpl();//c.testC();////System.out.println("使用Facade----------------------〉");// 使用了Facadenew Facade().test();}}
至此,代碼完全貼上。
第四:面板模式的本質:封裝互動,簡化調用。
它體現了“最少知識原則”
第五:面板模式的優缺點
優點:鬆散耦合,簡單易用,更好的劃分訪問的層次
缺點:過多的或者不合理的facade也容易讓人迷惑.
第六:何時選用面板模式
如果希望為一個複雜的子系統提供一個簡單介面的時候,從而簡化用戶端的應用
如果希望用戶端程式和抽象類別實現鬆散耦合,可以考慮使用面板模式
如果構建多層結構的系統,可以鬆散各層結構之間的依賴關係。
第七:facade成為介面以後,有一個好處就是可以選擇性的暴露介面,但是如何避免介面汙染呢,請看範例
package cn.javass.dp.facade.example4;public interface AModuleApi {public void a1();public void a2();public void a3();}
package cn.javass.dp.facade.example4;public interface BModuleApi {//對子系統外部public void b1();//子系統內部使用public void b2();//子系統內部使用public void b3();}
package cn.javass.dp.facade.example4;public interface CModuleApi {//對子系統外部public void c1();//子系統內部使用public void c2();//子系統內部使用public void c3();}
package cn.javass.dp.facade.example4;public interface FacadeApi {public void a1();public void b1();public void c1();public void test();//對外提供的組合方法,同前面Facade類的方法一樣}
這樣的話,一個模組中的介面定義分為兩部分,一部分是給子系統外部使用的【例如a1()】,一部分是給子系統內部的模組之間的相互調用使用的【例如a2(),a3()】,有了facade介面,那麼用於子系統內部的介面功能就不會暴露給子系統的外部了。