外觀模式
定義
外觀模式也叫門面模式
外觀模式(Facade),為子系統中的一組接口提供一個一致的界面,此模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。
適用范圍
1、解決易用性問題
門面模式可以用來封裝系統的底層實現,隱藏系統的復雜性,提供一組更加簡單易用、更高層的接口。
2、解決性能問題
我們通過將多個接口調用替換為一個門面接口調用,減少網絡通信成本,提高App客戶端的響應速度。
假設有一個系統A,提供了a、b、c、d四個接口。系統B完成某個業務功能,需要調用A系統的a、b、d接口。利用門面模式,我們提供一個包裹a、b、d接口調用的門面接口x,給系統B直接使用。
3、解決分布式事務問題
這個直接來個栗子吧
比如我們現在設計微服務,一個用戶服務,一個金幣服務。每個服務都提供對外的增刪查改等操作。現在我們有一個需求,新用戶登陸之后送用戶金幣。簡單地調用,肯定是創建一個用戶信息,之后調用金幣服務給這個用戶加金幣。當然實際地開發中我們肯定要考慮分布式事務,保障這兩個操作肯定能一起成功或失敗,不能出現一個成功一個失敗的場景。當然我們常規的做法肯定是引入分布式框架,或者補償的機制來處理。
其實借助於門面模式的思想也是可以處理,我們可以設計一個包裹這兩個操作的新接口,讓新接口在一個事務中執行兩個SQL操作。
代碼實現
假設有一個系統A,提供了a、b、c、d四個接口。系統B完成某個業務功能,需要調用A系統的a、b、d接口。利用門面模式,我們提供一個包裹a、b、d接口調用的門面接口x,給系統B直接使用。
type User struct {
}
func (u *User) GetUser(userId int) {
fmt.Println("獲取用戶的信息")
}
type GoldCoin struct {
}
func (g *GoldCoin) GetUserGoldCoin(userId int) {
fmt.Println("獲取用戶金幣的信息")
}
type Order struct {
}
func (o *Order) GetUserOrder(userId int) {
fmt.Println("獲取用戶訂單信息")
}
func GetUserInfo(userId int) {
user := User{}
user.GetUser(userId)
goldCoin := GoldCoin{}
goldCoin.GetUserGoldCoin(userId)
order := Order{}
order.GetUserOrder(userId)
}
放一張結構圖

優點
-
對客戶屏蔽子系統組件,減少了客戶處理的對象數目並使得子系統使用起來更加容易。
-
實現了子系統與客戶之間的松耦合關系,這使得子系統的組件變化不會影響到調用它的客戶類,只需要調整外觀類即可。
-
降低了大型軟件系統中的編譯依賴性,並簡化了系統在不同平台之間的移植過程,因為編譯一個子系統一般不需要編譯所有其他的子系統。一個子系統的修改對其他子系統沒有任何影響,而且子系統內部變化也不會影響到外觀對象。
-
只是提供了一個訪問子系統的統一入口,並不影響用戶直接使用子系統類。
缺點
-
不能很好地限制客戶使用子系統類,如果對客戶訪問子系統類做太多的限制則減少了可變性和靈活性。
-
在不引入抽象外觀類的情況下,增加新的子系統可能需要修改外觀類或客戶端的源代碼,違背了“開閉原則”。
關於接口粒度的思考
接口粒度設計得太大,太小都不好。太大會導致接口不可復用,太小會導致接口不易用。在實際的開發中,接口的可復用性和易用性需要“微妙”的權衡。
針對這個問題,王爭大佬給出了一個原則,可以作為參考,盡量保持接口的可復用性,但針對特殊情況,允許提供冗余的門面接口,來提供更易用的接口。
參考
【文中代碼】https://github.com/boilingfrog/design-pattern-learning/tree/master/外觀模式
【大話設計模式】https://book.douban.com/subject/2334288/
【極客時間】https://time.geekbang.org/column/intro/100039001
【外觀模式】https://design-patterns.readthedocs.io/zh_CN/latest/structural_patterns/facade.html
【原文地址】https://boilingfrog.github.io/2021/11/15/使用go實現外觀模式/