日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

簡介

適配器模式(Adapter)是最常用的結構型模式之一,在現實生活中,適配器模式也是處處可見,比如電源插頭轉換器,它可以讓英式的插頭工作在中式的插座上。

GoF 對它的定義如下:

Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.

簡單來說,就是適配器模式讓原本因為接口不匹配而無法一起工作的兩個類/結構體能夠一起工作

適配器模式所做的就是將一個接口 Adaptee,通過適配器 Adapter 轉換成 Client 所期望的另一個接口 Target 來使用,實現原理也很簡單,就是 Adapter 通過實現 Target 接口,并在對應的方法中調用 Adaptee 的接口實現。

Go語言實現GoF設計模式:適配器模式

 

UML 結構

Go語言實現GoF設計模式:適配器模式

 

場景上下文

在 簡單的分布式應用系統(示例代碼工程File not found · GitHub)中,db 模塊用來存儲服務注冊信息和系統監控數據,它是一個 key-value 數據庫。在 訪問者模式 中,我們為它實現了 Table 的按列查詢功能;同時,我們也為它實現了簡單的 SQL 查詢功能(將會在 解釋器模式 中介紹),查詢的結果是 SqlResult 結構體,它提供一個 toMap 方法將結果轉換成 map 。

為了方便用戶使用,我們將實現在終端控制臺上提供人機交互的能力,如下所示,用戶輸入 SQL 語句,后臺返回查詢結果:

Go語言實現GoF設計模式:適配器模式

 

終端控制臺的具體實現為 Console,為了提供可擴展的查詢結果顯示樣式,我們設計了 ConsoleRender 接口,但因 SqlResult 并未實現該接口,所以 Console 無法直接渲染 SqlResult 的查詢結果。

Go語言實現GoF設計模式:適配器模式

 

為此,我們需要實現一個適配器,讓 Console 能夠通過適配器將 SqlResult 的查詢結果渲染出來。示例中,我們設計了適配器 TableRender,它實現了 ConsoleRender 接口,并以表格的形式渲染出查詢結果,如前文所示。

Go語言實現GoF設計模式:適配器模式

 

代碼實現

// demo/db/sql.go
package db

// Adaptee SQL語句執行返回的結果,并未實現Target接口
type SqlResult struct {
    fields []string
    vals   []interface{}
}

func (s *SqlResult) Add(field string, record interface{}) {
    s.fields = Append(s.fields, field)
    s.vals = append(s.vals, record)
}

func (s *SqlResult) ToMap() map[string]interface{} {
    results := make(map[string]interface{})
    for i, f := range s.fields {
        results[f] = s.vals[i]
    }
    return results
}

// demo/db/console.go
package db

// Client 終端控制臺
type Console struct {
    db Db
}

// Output 調用ConsoleRender完成對查詢結果的渲染輸出
func (c *Console) Output(render ConsoleRender) {
    fmt.Println(render.Render())
}

// Target接口,控制臺db查詢結果渲染接口
type ConsoleRender interface {
    Render() string
}

// TableRender表格形式的查詢結果渲染Adapter
// 關鍵點1: 定義Adapter結構體/類
type TableRender struct {
    // 關鍵點2: 在Adapter中聚合Adaptee,這里是把SqlResult作為TableRender的成員變量
    result *SqlResult
}

// 關鍵點3: 實現Target接口,這里是實現了ConsoleRender接口
func (t *TableRender) Render() string {
    // 關鍵點4: 在Target接口實現中,調用Adaptee的原有方法實現具體的業務邏輯
    vals := t.result.ToMap()
    var header []string
    var data []string
    for key, val := range vals {
        header = append(header, key)
        data = append(data, fmt.Sprintf("%v", val))
    }
    builder := &strings.Builder{}
    table := tablewriter.NewWriter(builder)
    table.SetHeader(header)
    table.Append(data)
    table.Render()
    return builder.String()
}

// 這里是另一個Adapter,實現了將error渲染的功能
type ErrorRender struct {
    err error
}

func (e *ErrorRender) Render() string {
    return e.err.Error()
}

客戶端這么使用:

func (c *Console) Start() {
    fmt.Println("welcome to Demo DB, enter exit to end!")
    fmt.Println("> please enter a sql expression:")
    fmt.Print("> ")
    scanner := bufio.NewScanner(os.Stdin)
    for scanner.Scan() {
        sql := scanner.Text()
        if sql == "exit" {
            break
        }
        result, err := c.db.ExecSql(sql)
        if err == nil {
            // 關鍵點5:在需要Target接口的地方,傳入適配器Adapter實例,其中創建Adapter實例時需要傳入Adaptee實例
            c.Output(NewTableRender(result))
        } else {
            c.Output(NewErrorRender(err))
        }
        fmt.Println("> please enter a sql expression:")
        fmt.Print("> ")
    }
}

在已經有了 Target 接口(ConsoleRender)和 Adaptee(SqlResult)的前提下,總結實現適配器模式的幾個關鍵點:

  1. 定義 Adapter 結構體/類,這里是 TableRender 結構體。
  2. 在 Adapter 中聚合 Adaptee,這里是把 SqlResult 作為 TableRender 的成員變量。
  3. Adapter 實現 Target 接口,這里是 TableRender 實現了 ConsoleRender 接口。
  4. 在 Target 接口實現中,調用 Adaptee 的原有方法實現具體的業務邏輯,這里是在 TableRender.Render() 調用 SqlResult.ToMap() 方法,得到查詢結果,然后再對結果進行渲染。
  5. 在 Client 需要 Target 接口的地方,傳入適配器 Adapter 實例,其中創建 Adapter 實例時傳入 Adaptee 實例。這里是在 NewTableRender() 創建 TableRender 實例時,傳入 SqlResult 作為入參,隨后將 TableRender 實例傳入 Console.Output() 方法。

擴展

適配器模式在 Gin 中的運用

Gin 是一個高性能的 Web 框架,它的常見用法如下:

// 用戶自定義的請求處理函數,類型為gin.HandlerFunc
func myGinHandler(c *gin.Context) {
    ... // 具體處理請求的邏輯
}

func mAIn() {
    // 創建默認的route引擎,類型為gin.Engine
    r := gin.Default()
    // route定義
    r.GET("/my-route", myGinHandler)
    // route引擎啟動
    r.Run()
}

在實際運用場景中,可能存在這種情況。用戶起初的 Web 框架使用了 Go 原生的 .NET/http,使用場景如下:

// 用戶自定義的請求處理函數,類型為http.Handler
func myHttpHandler(w http.ResponseWriter, r *http.Request) {
    ... // 具體處理請求的邏輯
}

func main() {
    // route定義
    http.HandleFunc("/my-route", myHttpHandler)
    // route啟動
    http.ListenAndServe(":8080", nil)
}

因性能問題,當前客戶準備切換至 Gin 框架,顯然,myHttpHandler 因接口不兼容,不能直接注冊到 gin.Default() 上。為了方便用戶,Gin 框架提供了一個適配器 gin.WrapH,可以將 http.Handler 類型轉換成 gin.HandlerFunc 類型,它的定義如下:

// WrapH is a helper function for wrapping http.Handler and returns a Gin middleware.
func WrapH(h http.Handler) HandlerFunc {
	  return func(c *Context) {
		  h.ServeHTTP(c.Writer, c.Request)
	  }
}

使用方法如下:

// 用戶自定義的請求處理函數,類型為http.Handler
func myHttpHandler(w http.ResponseWriter, r *http.Request) {
    ... // 具體處理請求的邏輯
}

func main() {
    // 創建默認的route引擎
    r := gin.Default()
    // route定義
    r.GET("/my-route", gin.WrapH(myHttpHandler))
    // route引擎啟動
    r.Run()
}

在這個例子中,gin.Engine 就是 Client,gin.HandlerFunc 是 Target 接口,http.Handler 是 Adaptee,gin.WrapH 是 Adapter。這是一個 Go 風格的適配器模式實現,以更為簡潔的 func 替代了 struct。

典型應用場景

  • 將一個接口 A 轉換成用戶希望的另外一個接口 B,這樣就能使原來不兼容的接口 A 和接口 B 相互協作。
  • 老系統的重構。在不改變原有接口的情況下,讓老接口適配到新的接口。

優缺點

優點

  1. 能夠使 Adaptee 和 Target 之間解耦。通過引入新的 Adapter 來適配 Target,Adaptee 無須修改,符合開閉原則。
  2. 靈活性好,能夠很方便地通過不同的適配器來適配不同的接口。

缺點

  1. 增加代碼復雜度。適配器模式需要新增適配器,如果濫用會導致系統的代碼復雜度增大。

與其他模式的關聯

適配器模式 和 裝飾者模式、代理模式 在 UML 結構上具有一定的相似性。但適配器模式改變原有對象的接口,但不改變原有功能;而裝飾者模式和代理模式則在不改變接口的情況下,增強原有對象的功能。

文章配圖

可以在 用Keynote畫出手繪風格的配圖 中找到文章的繪圖方法。

參考

[1] 【Go實現】實踐GoF的23種設計模式:SOLID原則, 元閏子

[2] Design Patterns, Chapter 4. Structural Patterns, GoF

[3] 適配器模式, refactoringguru.cn

[4] Gin Web Framework, Gin

分享到:
標簽:語言
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定