Go 代碼應用設計模式: 裝飾器模式、外觀模式

我們再來看看結構型設計模式的另一種,裝飾器模式

裝飾器模式和橋接模式有着類似的概念,橋接模式是把若干類別的對象其中具體的類橋接在一起,組合成一個具體的組合對象。而裝飾器模式也是把多個對象組合在一起,但是對類別的概念比較模糊。

我們還是通過一個實際的案例來理解裝飾器模式。假設有個通知類,在系統觸發報警時給用戶發送郵件。而此時又引入了短信、飛書、電話通知,且在不同報警等級發送不同組合的通知,你會怎樣設計這個實現呢?我們先來看看兩種實現方式,然後再來討論兩者的利弊。

方案一:

首先把各種通知類型封裝自己的類,以及自己的 Send 方法,

type SmsNotify struct {
}
type TelNotify struct {
}
type FlyNotify struct {
}
func (s SmsNotify) Send() {
  fmt.Println("send sms msg")
}
func (t TelNotify) Send() {
  fmt.Println("call tel")
}
func (f FlyNotify) Send() {
  fmt.Println("send fly msg")
}

然後定義發送消息的結構,且實現了 Notifier 接口:

type Notifier interface {
  Send()
}
type PanicNotifier struct {
  tel TelNotify
  fly FlyNotify
}
func (p PanicNotifier) Send() {
  p.tel.Send()
  p.fly.Send()
}
type ErrorNotifier struct {
  fly FlyNotify
  sms SmsNotify
}
func (e ErrorNotifier) Send() {
  e.fly.Send()
  e.sms.Send()
}

方案二,具體的消息結構不變,而是發送消息類不區分報警等級,而是通過成員函數的實現來區分。

type Notifier struct {
  tel TelNotify
  fly FlyNotify
  sms SmsNotify
}
func NewNotifier(t TelNotify, f FlyNotify, s SmsNotify) Notifier{
  return Notifier{t,fly,sms}
}
func (n Notifier) SendPanic() {
  n.tel.Send()
  n.fly.Send()
}
func (n Notifier) SendError() {
  n.fly.Send()
  n.sms.Send()
}

方案一和二的實現都可以滿足業務需求,如果我們再考慮長遠一點,未來有更多種發送消息方式引入,方案二中得對 Notifier 結構進行成員添加。我們得爲 Notifier 的構造方法(這裏的 NewNotifier 方法)我們得初始化所有的成員 ,當添加的成員越來越多,我們需要初始化的成員也越來越多。而我們如果單單隻要發送 SendPanic 方法,那麼不需要出示好的 sms 成員也得進行初始化。

當然這裏有人說我可以傳個 nil,但是如果傳入 nil,又使用該對象誤使用了 SendError 要麼需要邏輯來處理不崩潰,要麼返回錯誤(成員 nil 判斷)。這個時候外層調用又得對錯誤進行處理。

當需要有類似不同對象組合方式的需求時,這裏建議用方案一,也就是裝飾模式來處理,雖然定義比較多,但是減少了在對象使用過程中的出錯。方案二也是裝飾模式實現的,但是這個裝飾有些不那麼聰明。

就像一個人有很多的衣服,在每個季節應該有不同衣服的搭配,而方案二就像是我爲了避免換衣服,無論什麼季節,那我把所有的衣服都穿上吧。

接下來我們再講講創建模式中的外觀模式

外觀模式正好就是上述所說的 “把所有衣服都穿上” 的這種情況。但是這裏使用的場景與上述的案例有些不同。假設我們有一個通知類,需要發送所有類型的通知,而對於使用者無需關注具體發送了哪些通知,只需要調用成員方法即可。例如以下代碼,用戶只需要關注 Send 而不需要知道具體發了哪些通知:

func (n Notifier) Send() {
  n.tel.Send()
  n.fly.Send()
  n.sms.Send()
}

通知的案例用在外觀模式比較牽強,大家可以想想例如轉賬(或者很多流程很繁瑣的過程),對於使用者只需要知道我要完成轉賬功能就行,我不用關注你的細節,細節可能包括賬戶審覈、餘額查詢、發起轉賬等等過程。

通過以上代碼示例來看,模式並沒有好壞之分,模式其實也都是長久的代碼實踐總結出來的概念,只有與實際的使用場景結合才能選擇最適合的模式。大家可以通過上述代碼和分析來了解裝飾器模式、外觀模式在代碼和使用場景上的不同。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/v4YbPE2SPiGYyToZeXmxQQ