Go 代碼應用設計模式: 適配器模式、橋接模式、組合模式

今天開始我們進入結構型設計模式講解。結構型是把類和對象組裝成一個結構(組合在一起使用),從而使代碼的運行效率更高、可擴展性更好。

首先我們瞭解下適配器模式

聽到 “適配器” 我們很容易聯想到當我們買的港版手機等電子產品時,配置的充電頭和行貨的不一樣。(不過現在手機已經不配置充電頭了🤦‍♂️)如果購買了港版手機在內陸使用,這個時候我們需要再額外購買個轉接頭纔可以使用這個充電器給手機充電。這個轉接頭就是“適配器”。

在我們的實際代碼中也存在着各種適配場景,例如某個處理 json 格式參數的接口,而由於某個場景輸入的是 xml 格式的參數,我們需要在接口外寫一個適配函數將 xml 格式轉爲 json,未來的輸入可能還有 yaml、toml 等格式,都需要增加適配函數而無需對原有的邏輯進行處理。

我們先來看下一個簡單的 Go 代碼實現:

type Phone struct {
}
func (p Phone) Charge() {
  fmt.Println("手機充電")
}
// 適配器
type HKAdaptor struct {
  phone Phone
}
func (a HKAdaptor) Charge() {
  fmt.Println("使用適配器給手機充電")
  a.phone.Charge()
}
func main() {
  // 在國內直接給國內版本手機充電
  p := Phone{}
  p.Charge()
  // 在香港給國內版本手機充電
  adaptor := HKAdaptor{
    phone: p,
  }
  adaptor.Charge()
}

這裏 HKAdaptor 就是給手機裝上的適配器,從而在香港也可以給手機充電。這個時候我們可以再想一下,如果我們出差去香港還帶有筆記本,按照上面的實現代碼,還得帶一個給筆記本充電頭的適配器嗎?

其實轉換器是通用的,無需再給筆記本帶一個另一種類型的適配器,代碼的實現就是可以複用 HKAdaptor 結構,我們再看下更通用的適配代碼需要怎樣改動呢?

type Digital interface {
  Charge()
}
type Phone struct {
}
func (p Phone) Charge() {
  fmt.Println("手機充電")
}
type Computor struct {
}
func (c Computor) Charge() {
  fmt.Println("電腦充電")
}
// 適配器
type HKAdaptor struct {
  digital Digital
}
func (a HKAdaptor) Charge() {
  fmt.Println("使用適配器給電子產品充電")
  a.digital.Charge()
}
func main() {
  // 在國內直接給國內版本手機充電
  p := Phone{}
  p.Charge()
  // 在國內直接給國內版本電腦充電
  c := Computor{}
  c.Charge()
  // 在香港給國內版本手機充電
  adaptor := HKAdaptor{
    digital: p,
  }
  adaptor.Charge()
  // 手機充完電了給電腦充電
  adaptor.digital = c
  adaptor.Charge()
}

只需要定義一個含有 Charge() 方法的接口 Digital,同時適配器中裝載的就不是具體的 Phone 或者 Computer 了,而是一個更抽象的接口 Didital。這樣無論是手機、電腦或者或者未來你買的各種有充電功能的電子產品(實現了 Charge() 方法的類)都可以使用這個適配器充電了。

接下來我們再聊聊橋接模式

橋接的概念是分別把兩種類型的對象鏈接起來使用。例如有形狀的類:圓形、矩形、三角形,顏色類:紅、黃、藍。如果需要兩兩組和使用,一共有 3*3 = 9 種組合的實際對象。那麼爲了實現這 9 個對象我們需要定義 9 個類來處理嗎?我們看下在橋接模式中實現的代碼:

type Color interface {
  printer()
}
type Red struct {
}
func (r Red) printer() {
  fmt.Println("with red")
}
type Shape interface {
  printer()
  setColor(Color)
}
type Circle struct {
  color Color
}
func (c Circle) printer() {
  fmt.Println("Circle")
  c.color.printer()
}
func (c Circle) setColor(color Color) {
  c.color = color
}

可以類似代碼示例中的定義定義其他顏色和形狀的類,從而實現了顏色和形狀的橋接,避免過多的類定義。後續擴展新的顏色和形狀,也只需定義當前對象就行,而無需關注兩者與其他類型的所有類的組合使用問題。

我們再來看下組合模式

這裏緊接着講組合模式是希望和上一個橋接模式模式概念區分開,因爲在橋接模式中有兩兩組合的概念。組合模式的組合表示所有不同對象的處理組合在一起,不用單獨的爲每個對象定義單獨的特殊處理。

模擬計算機的文件系統,有文件或者文件夾,我們需要查找某個關鍵字的內容時,可以將文件、文件夾同等對待處理。

type Component interface {
  Search(w string)
}
type File struct {
  Name string
}
type Fold struct {
  Components []Component
  Name       string
}
func (f File) Search(w string) {
  fmt.Println("search file")
}
func (f Fold) Search(w string) {
  fmt.Println("search file")
  for _, c := range f.Components {
    c.Search(w)
  }
}

 這裏 File 、Fold 都實現了 componet 的 Search 方法,所以在文件夾下無論是文件和文件夾都可以直接使用 Search 方法,真正的執行交給各自對象去處理,而在外層使用者無需關注具體是什麼類型。

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