Go 代碼應用設計模式: 模板方法模式

我們接着看行爲模式中的模板方法模式

模板方法模式爲定義了一系列的行爲步驟規範(方法、函數),而在一些特殊的場景下可能不同的步驟有着不同的處理方法需要特別的實現。

我們去超市買東西有尋找商品、查看價格、支付以下步驟。而在查看商品價格這一步,大多數商品可以直接看貨櫃上的價格,而水果、素菜只能看到單價,如果需要知道實際的價格還得進行稱重。

我們的代碼邏輯中處理查看價格或許直接使用 if 判斷了,如果是水果則還需要進行計算,例如代碼:

func (g *Goods) getPrice() float32 {
  if g.goodsType == GoodsTypeFruit {
    return g.price * g.weight
  }
  return g.price
}

當不同計價商品的種類越來越多,這裏的 if 也將越來越長,此時代碼的閱讀性、可維護性越來越差。

我們來看下模版方法模式是如何處理上述過程的。

const (
  GoodsTypeNormal = 0
  GoodsTypeFruit  = 1
)
type Goods struct {
  name   string
  price  float32
  weight float32
}
type calcu interface {
  searchGoods(string) Goods
  getPrice(Goods) float32
  pay(float32)
}
type Normal struct {
}
func (Normal) searchGoods(name string) Goods {
  return Goods{name, 0, 0}
}
func (Normal) getPrice(g Goods) float32 {
  return g.price
}
func (Normal) pay(price float32) {
  fmt.Printf("已支付 %f 元\n", price)
}
type Fruit struct {
  Normal
}
func (f Fruit) searchGoods(name string) Goods {
  return f.Normal.searchGoods(name)
}
func (Fruit) getPrice(g Goods) float32 {
  return g.price * g.weight
}
func (f Fruit) pay(price float32) {
  f.Normal.pay(price)
}

我們可以看到這裏 Normal 爲模板, Fruit 除了自己實現了 getPrice 方法,其餘方法都是使用的模版類提供的方法,避免如反例中使用過長的 if。

我們回過頭來看下所有的行爲模式,正如我們在設計模式第一講中傳達的觀點,儘量避免使用過多的 if。行爲模式的最基本思想是把不同判斷下的邏輯處理放到不同的類中去實現

當我們發現代碼邏輯中的 ifelse、switch 層級過多,過長,這個時候我們需要根據實際場景來選擇相應的行爲模式完成代碼實現,這樣代碼更美觀且具有高可維護性。

但是在我們的實際開發中,當我們又新增了某種場景時,我們總是在以下兩個選擇中徘徊:

  1. 避免對原來的代碼修改而產生問題,我也往後面加個 else if;

  2. 有代碼潔癖性,把以前的 if 套用某個設計模式重新實現,並增加一個場景。

我在工作之初大多數情況下會選擇第二項。但是也爲此付出過慘痛的代價,由於對業務沒有十足的掌握,老的代碼邏輯未完全梳理清楚而做大的改動而產生線上事故。

隨着工作年限的增長有一種思想可能會更多的在腦海裏佔據優勢,在業務驅動的公司,業務纔是賺錢的,你的代碼只是實現賺錢的工具而已。你把代碼寫得再優秀,也沒法把價值體現出來。但是一旦出現了事故,會讓你之前所有的付出打了水漂。當出現過事故,嚐到疼痛感後,在下一次出現同樣的情形時,大多數人可能會選擇第一項了,大量的去改前人的代碼有些得不償失。

以上只是自己工作的感悟,相信大多數讀者也有同樣的困擾。也暢想希望設計模式能夠深入每一位開發人員的腦海裏,大家寫的代碼都很優秀,就不存在難以抉擇的囧境了。

有關設計模式的講解就到這裏了,這一系列並沒有把所有的設計模式一一列舉出來,有少許設計模式通過 Go 代碼實現與其他的模式類似,就沒有再闡述了,不瞭解的讀者,可自行閱讀設計模式相關教程。

下圖爲完整的設計模式及其分類:

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