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