作者:zuoxiaolong8810(左瀟龍),轉載請注明出處,特別說明:本博文來自博主原博客,為保證新博客中博文的完整性,特復制到此留存,如需轉載請注明新博客地址即可。
各位好,LZ今天給各位分享一個不太熟悉的面孔,但卻是我們最經常使用的設計模式,外觀模式。
定義:外觀模式是軟件工程中常用的一種軟件設計模式。它為子系統中的一組接口提供一個統一的高層接口。這一接口使得子系統更加容易使用。
該定義引自百度百科,它的表現很簡單,將一系列子接口的功能進行整理,從而產生一個更高層的接口。
相信做JAVA的各位大部分是WEB開發,那么肯定都對XXXDao,XXXService非常熟悉了,這顯然和外觀模式有一腿。當然,還有一大部分是android開發,LZ沒接觸過android開發,但是LZ大膽的想象,在移動領域的JAVA開發,應該也有類似的情況發生。
接下來,我們來看看外觀模式的標准類圖。
上述便是外觀模式的類圖,它主要由兩部分組成,一部分是子系統(包括接口,實現類,等等),一部分是外觀接口和實現類,外觀接口負責提供客戶端定制的服務,外觀實現則負責組合子系統中的各個類和接口完成這些服務,外觀接口則是提供給客戶端使用的,這樣就解除了客戶端與子系統的依賴,而讓客戶端只依賴於外觀接口,這是一個優秀的解耦實踐。
下面LZ依然使用JAVA代碼將上述的類圖詮釋出來,我們來直觀的看看外觀模式的實現方式。首先是我們的子系統,它包括三個接口,三個實現,LZ這里一並給出。
package com.facade; public interface Sub1 { void function1(); }
package com.facade; public interface Sub2 { void function2(); }
package com.facade; public interface Sub3 { void function3(); }
package com.facade; public class Sub1Impl implements Sub1{ public void function1() { System.out.println("子系統中Sub1接口的功能"); } }
package com.facade; public class Sub2Impl implements Sub2{ public void function2() { System.out.println("子系統中Sub2接口的功能"); } }
package com.facade; public class Sub3Impl implements Sub3{ public void function3() { System.out.println("子系統中Sub3接口的功能"); } }
以上便是我們模擬出的一個子系統,那么現在便是我們最重要的接口出場的時候了,LZ給出Facade以及它的簡單實現。
package com.facade; public interface Facade { /* 下面隨便組裝幾個功能 */ void function12(); void function23(); void function123(); }
package com.facade; public class FacadeImpl implements Facade{ private Sub1 sub1; private Sub2 sub2; private Sub3 sub3; public FacadeImpl() { super(); this.sub1 = new Sub1Impl(); this.sub2 = new Sub2Impl(); this.sub3 = new Sub3Impl(); } public FacadeImpl(Sub1 sub1, Sub2 sub2, Sub3 sub3) { super(); this.sub1 = sub1; this.sub2 = sub2; this.sub3 = sub3; } public void function12() { sub1.function1(); sub2.function2(); } public void function23() { sub2.function2(); sub3.function3(); } public void function123() { sub1.function1(); sub2.function2(); sub3.function3(); } }
以上便是我們的外觀接口和實現類,它當中的功能一般是根據是客戶端的需要定制的,將客戶端的一個完整功能作為一個行為,然后調用子系統完成。下面我們看看客戶端的調用。
package com.facade; public class Client { public static void main(String[] args) { Facade facade = new FacadeImpl(); facade.function12(); System.out.println("-------------------------"); facade.function23(); System.out.println("-------------------------"); facade.function123(); /* 以上為使用了外觀模式的調用方式,以下為原始方式 */ System.out.println("-------------以下原始方式--------------"); Sub1 sub1 = new Sub1Impl(); Sub2 sub2 = new Sub2Impl(); Sub3 sub3 = new Sub3Impl(); sub1.function1(); sub2.function2(); System.out.println("-------------------------"); sub2.function2(); sub3.function3(); System.out.println("-------------------------"); sub1.function1(); sub2.function2(); sub3.function3(); } }
LZ在下面還給出了原始的調用方式,可以看出在外觀模式的作用下,我們客戶端只依賴外觀一個接口,而在原始的方式下,我們的客戶端依賴於整個子系統,所以外觀模式主要解決的是類之間的耦合過於復雜。
附上LZ運行結果。
以上便是標准的外觀模式展現,LZ下面再給出需要知曉的幾點。
1,實際使用當中,接口並不是必須的,雖說根據依賴倒置原則,無論是處於高層的外觀層,還是處於底層的子系統,都應該依賴於抽象,但是這會倒置子系統的每一個實現都要對應一個接口,從而導致系統的復雜性增加,所以這樣做並不是必須的。
2,外觀接口當中並不一定是子系統中某幾個功能的組合,也可以是將子系統中某一個接口的某一功能單獨暴露給客戶端。
3,外觀接口如果需要暴露給客戶端很多的功能的話,可以將外觀接口拆分為若干個外觀接口,如此便會形成一層外觀層。
上述LZ給出的第三點,便是為了引出我們標題當中的service,相信各位做過web開發的都見過我們項目中很多的service和dao(注:小型項目或許不需要service這一層),這一層service層,有一個非常重要的作用,就是為了方便我們管理項目中與業務邏輯相關的事物,而service層,其實是給我們的事務管理器提供了一個可以方便的配置切入點的事物管理層。
除了上述這個重要的功能外,service層同時也是組合dao層暴露給action的功能,dao層的各個類只是簡單的數據操作對象,它們不具有業務邏輯,而賦予了它們業務邏輯方便action調用的功臣,正是service這一層。各位可以想象一下,假設沒有service這一層,你的action當中有很多功能需要依賴多少個dao才可以完成工作。
同時在WEB項目中,有的項目會抽象出一層service接口和一層dao接口,這是為了降低客戶端(這里的客戶端可以認為是action)與業務實現細節以及service外觀層與數據操作實現細節的耦合,而有的項目則沒有抽象層,這也並非就是不合適的。
首先添加抽象層會大大的加劇項目的類文件數量,從而使項目的復雜性增加,而且在項目剛進入開發的時候,往往接口是不穩定的,因為我們經常會需要要給某一個service添加一個方法,而為了將方法暴露給客戶端(即action),我們必須將該方法添加到對應的接口當中。
所以針對這一情況,我們更好的做法是等到接口行為相對穩定時,再考慮是否要重構去添加抽象的接口,而且現在的IDE工具都在一定程度上對重構進行了支持,比如eclipse就可以直接導出一個類的接口,所以我們完全可以在需要時快速的給項目添加抽象的接口層。
相比起觀察者模式,適配器模式等適合小規模使用的設計模式,外觀模式更多的是大范圍的使用,它會是很多時候支撐我們整個架構的設計思路。
鑒於此,LZ此處不再給出具體的service和dao的示例,各位的項目中到處都充斥着這種例子。
如果形象的去形容外觀模式在WEB中的應用,可以說它讓action和dao分了手,而插入了一個第三者service,斷開了action與dao的耦合,轉而使用更高層的service。
這里需要提醒各位的是,外觀模式並不是簡單的使用組合將功能組合起來,也就是說它的重點不在組合功能,而在於制作一個對外暴露的外觀。它一般是用來將一個子系統(注意,是一個子系統,也就是說外觀並不是簡單的幾個類的組合就是外觀模式了)的功能進行調配,暴露給客戶端一個外部的表象,使得客戶端與子系統斷開依賴關系。
由於外觀模式屬於一種“大”模式,所以我們平時很少會接觸到,但是有很多技術的應用,其實都有着外觀模式的影子。
比如webservice,它是給一個WEB應用提供一個外觀,讓客戶端可以調用一些接口去使用WEB應用當中的一些功能或者說服務。再比如API,中文名稱應用程序接口,它其實也可以看做是給底層的操作系統做了一層外觀,使程序猿在編程的時候可以直接使用外觀提供的接口,從而間接的指揮操作系統完成一些事情。
本次外觀模式的分享,LZ沒有像之前一樣寫一堆示例代碼給各位看,更多的是在討論外觀模式的應用場景和應用范圍,希望各位看完之后對外觀模式有一個宏觀的認識,而不是僅限於代碼層次的理解。
好了,本次外觀模式的分享就到此結束了,謝謝各位的收看,下期再見。
下期預告,命令模式。