外觀模式
定義
外觀模式也叫門面模式
外觀模式(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實現外觀模式/