深入淺出-外觀模式
外觀模式是一種使用頻率非常高的結構型設計模式,它通過引入一個外觀角色來簡化客戶端 與子系統之間的交互,爲複雜的子系統調用提供一個統一的入口,降低子系統與客戶端的耦 合度,且客戶端調用非常方便。
外觀模式概述 :
不知道大家有沒有比較過自己泡茶和去茶館喝茶的區別,如果是自己泡茶需要自行準備茶 葉、茶具和開水,如圖1(A)所示,而去茶館喝茶,最簡單的方式就是跟茶館服務員說想要一杯 什麼樣的茶,是鐵觀音、碧螺春還是西湖龍井?正因爲茶館有服務員,顧客無須直接和茶 葉、茶具、開水等交互,整個泡茶過程由服務員來完成,顧客只需與服務員交互即可,整個 過程非常簡單省事,如圖
在軟件開發中,有時候爲了完成一項較爲複雜的功能,一個客戶類需要和多個業務類交互, 而這些需要交互的業務類經常會作爲一個整體出現,由於涉及到的類比較多,導致使用時代 碼較爲複雜,此時,特別需要一個類似服務員一樣的角色,由它來負責和多個業務類進行交 互,而客戶類只需與該類交互。外觀模式通過引入一個新的外觀類(Facade)來實現該功能,外 觀類充當了軟件系統中的“服務員”,它爲多個業務類的調用提供了一個統一的入口,簡化了類 與類之間的交互。在外觀模式中,那些需要交互的業務類被稱爲子系統(Subsystem)。如果沒 有外觀類,那麼每個客戶類需要和多個子系統之間進行復雜的交互,系統的耦合度將很大, 如圖2(A)所示;而引入外觀類之後,客戶類只需要直接與外觀類交互,客戶類與子系統之間原 有的複雜引用關係由外觀類來實現,從而降低了系統的耦合度,如圖2(B)所示。
外觀模式中,一個子系統的外部與其內部的通信通過一個統一的外觀類進行,外觀類將客戶 類與子系統的內部複雜性分隔開,使得客戶類只需要與外觀角色打交道,而不需要與子系統 內部的很多對象打交道。
外觀模式定義如下: 外觀模式:爲子系統中的一組接口提供一個統一的入口。外觀模式定義 了一個高層接口,這個接口使得這一子系統更加容易使用。 Facade Pattern: Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.
外觀模式又稱爲門面模式,它是一種對象結構型模式。外觀模式是迪米特法則的一種具體實 現,通過引入一個新的外觀角色可以降低原有系統的複雜度,同時降低客戶類與子系統的耦 合度。
外觀模式沒有一個一般化的類圖描述,外觀模式的結構圖:
外觀模式包含如下兩個角色:
(1) Facade(外觀角色):在客戶端可以調用它的方法,在外觀角色中可以知道相關的(一個 或者多個)子系統的功能和責任;在正常情況下,它將所有從客戶端發來的請求委派到相應 的子系統去,傳遞給相應的子系統對象處理。
(2) SubSystem(子系統角色):在軟件系統中可以有一個或者多個子系統角色,每一個子系統 可以不是一個單獨的類,而是一個類的集合,它實現子系統的功能;每一個子系統都可以被 客戶端直接調用,或者被外觀角色調用,它處理由外觀類傳過來的請求;子系統並不知道外 觀的存在,對於子系統而言,外觀角色僅僅是另外一個客戶端而已。
模式實現
外觀模式的主要目的在於降低系統的複雜程度,在面向對象軟件系統中,類與類之間的關係 越多,不能表示系統設計得越好,反而表示系統中類之間的耦合度太大,這樣的系統在維護 和修改時都缺乏靈活性,因爲一個類的改動會導致多個類發生變化,而外觀模式的引入在很 大程度上降低了類與類之間的耦合關係。引入外觀模式之後,增加新的子系統或者移除子系 統都非常方便,客戶類無須進行修改(或者極少的修改),只需要在外觀類中增加或移除對 子系統的引用即可。從這一點來說,外觀模式在一定程度上並不符合開閉原則,增加新的子 系統需要對原有系統進行一定的修改,雖然這個修改工作量不大。
外觀模式中所指的子系統是一個廣義的概念,它可以是一個類、一個功能模塊、系統的一個 組成部分或者一個完整的系統。子系統類通常是一些業務類,實現了一些具體的、獨立的業 務功能,其典型代碼如下:
class SubSystemA { public void MethodA() { //業務實現代碼 } } class SubSystemB { public void MethodB() { //業務實現代碼 } } class SubSystemC { public void MethodC() { //業務實現代碼 } } |
在引入外觀類之後,與子系統業務類之間的交互統一由外觀類來完成,在外觀類中通常存在 如下代碼:
class Facade { private SubSystemA obj1 = new SubSystemA(); private SubSystemB obj2 = new SubSystemB(); private SubSystemC obj3 = new SubSystemC(); public void Method() { obj1.MethodA(); obj2.MethodB(); obj3.MethodC(); } } |
由於在外觀類中維持了對子系統對象的引用,客戶端可以通過外觀類來間接調用子系統對象 的業務方法,而無須與子系統對象直接交互。引入外觀類後,客戶端代碼變得非常簡單,典 型代碼如下:
class Program { static void Main(string[] args) { Facade facade = new Facade(); facade.Method(); } } |
舉個栗子:
實現一個能夠畫圖形的功能 三角形、方形、圓形
我們將創建一個 Shape 接口和實現了 Shape 接口的實體類。下一步是定義一個外觀類 ShapeMaker。
ShapeMaker 類使用實體類來代表用戶對這些類的調用。FacadePatternDemo,我們的演示類使用 ShapeMaker 類來顯示結果。
創建一個接口。
//Shape.java public interface Shape { void draw(); } |
創建實現接口的實體類。
public class Rectangle implements Shape { @Override public void draw() { System.out.println("Rectangle::draw()"); } } |
public class Square implements Shape { @Override public void draw() { System.out.println("Square::draw()"); } } |
public class Circle implements Shape { @Override public void draw() { System.out.println("Circle::draw()"); } } |
創建一個外觀類。
public class ShapeMaker { private Shape circle; private Shape rectangle; private Shape square; public ShapeMaker() { circle = new Circle(); rectangle = new Rectangle(); square = new Square(); } public void drawCircle(){ circle.draw(); } public void drawRectangle(){ rectangle.draw(); } public void drawSquare(){ square.draw(); } } |
使用該外觀類畫出各種類型的形狀。
public class FacadePatternDemo { public static void main(String[] args) { ShapeMaker shapeMaker = new ShapeMaker(); shapeMaker.drawCircle(); shapeMaker.drawRectangle(); shapeMaker.drawSquare(); } } |
執行程序,輸出結果:
Circle::draw()
Rectangle::draw()
Square::draw()
外觀模式效果與適用場景
外觀模式是一種使用頻率非常高的設計模式,它通過引入一個外觀角色來簡化客戶端與子系 統之間的交互,爲複雜的子系統調用提供一個統一的入口,使子系統與客戶端的耦合度降 低,且客戶端調用非常方便。外觀模式並不給系統增加任何新功能,它僅僅是簡化調用接 口。在幾乎所有的軟件中都能夠找到外觀模式的應用,如絕大多數B/S系統都有一個首頁或者 導航頁面,大部分C/S系統都提供了菜單或者工具欄,在這裏,首頁和導航頁面就是B/S系統 的外觀角色,而菜單和工具欄就是C/S系統的外觀角色,通過它們用戶可以快速訪問子系統, 降低了系統的複雜程度。所有涉及到與多個業務對象交互的場景都可以考慮使用外觀模式進 行重構。
模式優點
外觀模式的主要優點如下:
(1) 它對客戶端屏蔽了子系統組件,減少了客戶端所需處理的對象數目,並使得子系統使用起 來更加容易。通過引入外觀模式,客戶端代碼將變得很簡單,與之關聯的對象也很少。
(2) 它實現了子系統與客戶端之間的松耦合關係,這使得子系統的變化不會影響到調用它的客 戶端,只需要調整外觀類即可。
(3) 一個子系統的修改對其他子系統沒有任何影響,而且子系統內部變化也不會影響到外觀對 象。
5.2 模式缺點
外觀模式的主要缺點如下:
(1) 不能很好地限制客戶端直接使用子系統類,如果對客戶端訪問子系統類做太多的限制則減 少了可變性和靈活 性。
(2) 如果設計不當,增加新的子系統可能需要修改外觀類的源代碼,違背了開閉原則。
5.3 模式適用場景
在以下情況下可以考慮使用外觀模式:
(1) 當要爲訪問一系列複雜的子系統提供一個簡單入口時可以使用外觀模式。
(2) 客戶端程序與多個子系統之間存在很大的依賴性。引入外觀類可以將子系統與客戶端解耦,從而提高子系統的獨立性和可移植性。
(3) 在層次化結構中,可以使用外觀模式定義系統中每一層的入口,層與層之間不直接產生聯 系,而通過外觀類建立聯繫,降低層之間的耦合度。