適配器模式

適配器

適配器模式是設計模式中的一種:將一個類的接口轉換成客戶希望的另一個接口。適配器模式使得原本由於接口不兼容而不能一起工作的那些類可以一起工作。

OOP的實現

在一般的向對象的語言裏,可能是定義一個適配器類來實現的。Go 自然也是可以的。
比如已經有了一個自己的類,並且實現了一些方法:

type People struct {
    name string
}

func (p *People) Greet() {
    fmt.Printf("Hello, I am %s.\n", p.name)
}

現在有一個接口,下面是接口的定義和一個調用該接口的函數:

type Hello interface {
    SayHello()
}

func SayHello(s Hello) {
    s.SayHello()
}

問題來了,Peopel並沒有實現接口的方法,所以不能直接傳遞給 SayHello 函數。
解決方案一,直接爲People再寫一個方法就解決了。但是如果這個People是別的包裏的類型,就不能這麼做了。

解決方案二,爲原來的People定義一個別名類型,然後定義新的別名類型的方法來滿足接口。實現後大概是下面這樣的:

func main() {
    p2 := PeopleSayHello(p1)
    SayHello(&p2)
}

// 適配器
type PeopleSayHello People

func (p *PeopleSayHello) SayHello() {
    p1 := People(*p)
    p1.Greet()
}

由於類型轉換之後的新的類型是無法取地址的,所以必須要引入一個臨時變量。在main函數中多出這麼一行也不是很好。

解決方案三,定義一個新的結構體。這個是適配器模式的標準做法,網上也有很多類似的文章:

func main() {
    SayHello(&PeopleSayHello{&p1})
}

// 適配器
type PeopleSayHello struct {
    *People
}

func (p *PeopleSayHello) SayHello() {
    p.Greet()
}

這裏的方法實現也可以完全自定義。這裏只是簡單的調用了一個原結構體裏的方法,對於這種簡單的情況,有更輕量級的方式來適配接口。

爲函數類型定義方法

Go 語言的接口機制有一些不常見的特性。就是作爲一個函數類型,還可以擁有自己的方法。先看下面的實現:

func main() {
    SayHello(PeopleSayHello(p1.Greet))
}

// 適配器
type PeopleSayHello func()

func (f PeopleSayHello) SayHello() {
    f()
}

表達式PeopleSayHello(p1.Greet)並不是一個並不是一個函數調用,而是一個類型轉換。p1.Greet是一個方法值,就是如下類型的一個值:

func SayHello()

可以簡單的在這裏就把接口值就當做是一個函數值。
新的類型中定義了 SayHello 方法,現在滿足接口了,而這個方法就是調用函數本身,所以這裏的 PeopleSayHello 就是一個讓函數值滿足接口的一個適配器。
這裏用到的是方法值,其實本質上就是爲一個函數值定義方法來適配接口。所以,對於方法表達式或函數,也是同樣適用的。
在標準庫的 net\/http 中也有類似的用法,就是下面的 HandlerFunc 類型:

package http
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

完整的示例代碼

最後一個例子的完整的示例代碼。其他幾個例子也只是 main 函數中的調用,和適配器實現部分的代碼不同:

package main

import "fmt"

type People struct {
    name string
}

func (p *People) Greet() {
    fmt.Printf("Hello, I am %s.\n", p.name)
}

var p1 = People{"Adam"}

func init() {
    fmt.Print("init: ")
    p1.Greet()
}

// 接口
type Hello interface {
    SayHello()
}

func SayHello(s Hello) {
    s.SayHello()
}

func main() {
    SayHello(PeopleSayHello(p1.Greet))
}

// 適配器
type PeopleSayHello func()

func (f PeopleSayHello) SayHello() {
    f()
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章