文摘一:
有些地方外觀模式也被叫做門面模式,英文即Facade Pattern,提前說明一下。
試想這種情況,用戶添加了一條記錄,為了保持本地和服務器數據的一致性,除了在本地數據庫插入記錄,還需要把該記錄發送給服務器,讓服務器的數據庫也一并更新。
為了保持代碼的可讀性和低耦合性,一般會把不同的功能放在不同的文件不同的類中。如上面所說的情況,就需要一個DataHandler類更新本地數據庫,還需要一個NetworkClient類處理網絡請求。這兩個類有如下定義:
@interface DataHandler : NSObject
- (void)addItemToDatabase:(id)item;
@end
@interface NetworkClient : NSObject
- (void)addItemToServer:(id)item;
@end
每當數據有變化,都需要調用這兩個類的方法。如果DataHandler和NetworkClient這兩個類中的方法有變化,相應的需要修改很多調用者的代碼。
而采用外觀模式時,一個Facade將DataHandler和HTTPClient封裝在一起,然后提供一個新的接口。 現在定義一個Manager類如下:
@interface Manager : NSObject
- (void)addItem:(id)item;
@end
@implementation Manager
{
DataHandler *dataHandler;
NetworkClient *networkClient;
}
- (void)addItem:(id)item
{
[dataHandler addItemToDatabase:item];
[networkClient addItemToServer:item];
}
@end
現在每當用戶添加新數據時,只需調用Manager的addItem:方法就可以實現本地和服務器數據庫的操作。而且如果DataHandler類和NetworkClient類的方法有變化,也只需修改Manager中的代碼,對調用者并沒有影響。
外觀模式本質就是一種封裝,將零散的功能組合成一個,然后對外提供一個接口,使用者只需要知道這一個接口就可以了。這種模式大大降低了代碼的耦合性,對代碼重用和迭代開發很有幫助。
注意,在使用外觀模式時,各個子模塊的接口是無法被隱藏起來的,你也不能阻止調用者不使用集成的API而去單獨訪問各子模塊接口。
文摘二:
考慮使用外觀模式的情況一般分為三種情況。
第一種情況,設計初始階段,應該要有意識的將不同的兩個分層分離,層與層之間建立外觀Facade,這樣可以為復雜的子系統提供一個簡單的接口,使得耦合大大降低。
第二種情況,在開發階段子系統往往因為不斷的重構演化而變得越來越復雜,增加外觀Facade可以提供一個簡單的接口,減少它們之間的依賴。
第三種情況,在維護一個遺留的大型系統時,可能這個系統已經非常難以維護和擴展了,如果有新的需求,那么可以為新系統開發一個外觀Facade類,來提供設計粗糙或高度復雜的遺留代碼的比較清晰簡單的接口,讓新系統與Facade對象交互,Facade與遺留代碼交互所有復雜的工作,這樣可以保持較低的耦合度。
文摘三:
引言
在項目開發中,有時候會遇到這樣的一種情景:已有系統的各個子系統之間,隨著業務需求的發展,有了比較緊湊的耦合關系。現在需要利用這些子系統的功能,為移動端提供業務處理。我們該怎么應對這樣的業務需求呢?這就是本章外觀模式所要解決的問題。
進入正式講解前,我們先來分析一下兩種應對如上業務需求的方式:
方式一:移動端直接調用各個子系統的功能,和各個子系統之間形成緊耦合的關系,如下圖所示:
方式二:提供一個高層接口,該高層接口負責和子系統進行交互,并向移動端提供需要使用的接口,如下圖所示:
從上面兩種方式的圖式結構可以看到,對移動端來說,方式二比方式一要好用很多,因為在方式二中,移動端不需要知道各個子系統的邏輯,只需要和高層接口交互就可以了。實際上方式二,就是我們這里要說的外觀模式了。
定義
“為子系統中的一組接口提供一個統一的接口。外觀模式定義了一個更高層次的接口,這個接口使得這一子系統更加容易使用。”
最初的定義出現于《設計模式》(Addison-Wesley,1994)。
這個定義,通過上面引言的圖示講解,應該很好理解了,這里再分析一下定義中的兩個重要角色:
外觀角色:就是引言圖示中的“高層接口”,客戶端可以調用這個角色的方法;另外,該角色知道相關的子系統的功能和責任。
子系統角色:可以同時有一個或者多個子系統。每一個子系統都不是一個單獨的類,而是一個類的集合。每一個子系統都可以被客戶端直接調用,或者被外觀角色調用。
結構圖
示例
生活中,應用外觀模式的例子很多,比如去飯館吃飯,我們不需要關注菜的選料、烹調等過程,只需要和服務員進行交互:服務員給我們菜譜(相當于就是外觀模式的高級接口),我們選菜(調用接口),就可以享受美食。
這里,我們用另一個生活中的例子來進行解說。不知道大家有沒有通過旅行社報團出去旅游的經歷?這是一個很好的外觀模式的應用。我們選擇好景點之后,旅行社會幫我們聯系大巴、旅館、飯店、景點門票以及景點服務等事情,這些事情我們都不需要親自去安排,這就是外觀模式的便利之處:可以使得客戶端的接口更簡單。
下面列出應用外觀模式實現旅行社報團旅游的結構圖:
如果不應用外觀模式,我們(上圖中的Client),就得自己去聯系交通工具、預定旅館、飯館、景點門票等,相信這樣的旅程,大家會感覺很累。有了外觀角色(上圖中的Facade),它會幫我們去處理這些事情。完整代碼大家可以下載查看,這里只貼出部分源碼。
Facade.m(部分源碼):
- (id)init {
self = [super init];
if (self != nil) {
_transportation = [[Transportation alloc] init];
_hotel = [[Hotel alloc] init];
_restaurant = [[Restaurant alloc] init];
_attractions = [[Attractions alloc] init];
}
return self;
}
- (void)travel {
[_transportation selTransportation];
[_hotel selHotel];
[_restaurant selRestaurant];
[_attractions selAttractions];
}
- (void)dealloc23 {
[_transportation release];
[_hotel release];
[_restaurant release];
[_attractions release];
[super dealloc];
}
從源碼可以看到,外觀類調用了交通工具類、旅館類、飯館類、景點類。下面看看客戶端調用代碼:
Facade *facade = [[Facade alloc] init];
[facade travel];
[facade release];
客戶端代碼只需要和外觀類進行交互。
小結
通過上面的講解,我們來分析一下外觀模式的特點:
Facade設計模式注重從架構的層次去看整個系統,而不是單個類的層次。很多時候,它是一種架構設計模式,比如我們常用的三層架構。
Facade模式簡化了整個系統的接口,同時對于系統內部(子系統)與外部程序(Client)來說,也達到了一種“解耦”的效果。
根據外觀模式的特點,我們可以在以下情況中使用Facade模式:
不需要使用一個復雜系統的所有功能,只需要使用系統的部分功能時,那么應用Fa?ade模式提供一個高層接口將比直接調用原系統的接口簡單得多。
希望封裝或者隱藏原系統的接口時,可以考慮使用外觀模式。
希望使用原系統的部分功能,而且還希望增加一些新的功能。
構建一個具有層次結構的子系統時,使用Facade模式定義子系統中每層的高級接口。如果子系統之間是相互依賴的,你可以讓它們僅通過Facade進行通訊,從而簡化了它們之間的依賴關系。
循自然之道,撫浮躁之心