国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

依賴注入與控制反轉:優化Go語言REST API客戶端

開發 前端
在這篇文章中,我展示了如何以及為什么在Go中使用DI和IoC。正確使用DI/IoC可以導致更易于測試和維護的代碼,特別是在代碼庫不斷增長時。雖然代碼示例是用Go編寫的,但這里描述的原則同樣適用于其他編程語言。

在這篇文章中,我將探討依賴注入(DI)和控制反轉(IoC)是什么,以及它們的重要性。作為示例,我將使用Monibot的REST API客戶端。讓我們開始吧:

一個簡單的客戶端實現

我們從一個簡單的客戶端實現開始,允許調用者訪問Monibot的REST API,具體來說,是為了發送指標值。客戶端的實現可能如下所示:

package monibot

type Client struct {
}

func NewClient() *Client {
    return &Client{}
}

func (c *Client) PostMetricValue(value int) {
    body := fmt.Sprintf("value=%d", value)
    http.Post("https://monibot.io/api/metric", []byte(body))
}

這里有一個客戶端,提供了PostMetricValue方法,該方法用于將指標值上傳到Monibot。我們的庫的用戶可能像這樣使用它:

import "monibot"

func main() {
    // 初始化API客戶端
    client := monibot.NewClient()
    // 發送指標值
    client.PostMetricValue(42)
}

依賴注入

現在假設我們想對客戶端進行單元測試。當所有HTTP發送代碼都是硬編碼的時候,我們如何測試客戶端呢?對于每次測試運行,我們都需要一個“真實”的HTTP服務器來回答我們發送給它的所有請求。不可取!我們可以做得更好:讓我們將HTTP處理作為“依賴”;讓我們發明一個 Transport 接口:

package monibot

// Transport傳輸請求。
type Transport interface {
    Post(url string, body []byte)
}

讓我們再發明一個具體的使用HTTP作為通信協議的Transport:

package monibot

// HTTPTransport是一個使用HTTP協議傳輸請求的Transport。
type HTTPTransport struct {
}

func (t HTTPTransport) Post(url string, data []byte) {
    http.Post(url, data)
}

然后讓我們重寫客戶端,使其“依賴”于一個Transport 接口:

package monibot

type Client struct {
    transport Transport
}

func NewClient(transport Transport) *Client {
    return &Client{transport}
}

func (c *Client) PostMetricValue(value int) {
    body := fmt.Sprintf("value=%d", value)
    c.transport.Post("https://monibot.io/api/metric", []byte(body))
}

現在,客戶端將請求轉發到它的Transport依賴。當創建客戶端時,transport(客戶端的依賴項)被“注入”到客戶端中。調用者可以這樣初始化一個客戶端:

import "monibot"

func main() {
    // 初始化API客戶端
    var transport monibot.HTTPTransport
    client := monibot.NewClient(transport)
    // 發送指標值
    client.PostMetricValue(42)
}

單元測試

現在我們可以編寫一個使用“偽造”Transport的單元測試:

// TestPostMetricValue確保客戶端向REST API發送正確的POST請求。
func TestPostMetricValue(t *testing.T) {
    transport := &fakeTransport{}
    client := NewClient(transport)
    client.PostMetricValue(42)
    if len(transport.calls) != 1 {
        t.Fatal("期望1次傳輸調用,但是是%d次", len(transport.calls))
    }
    if transport.calls[0] != "POST https://monibot.io/api/metric, body=\\"value=42\\"" {
        t.Fatal("錯誤的傳輸調用 %q", transport.calls[0])
    }
}

// 偽造的Transport是單元測試中使用的Transport。
type fakeTransport struct {
    calls []string
}

func (f *fakeTransport) Post(url string, body []byte) {
    f.calls = append(f.calls, fmt.Sprintf("POST %v, body=%q", url, string(body)))
}

添加更多的Transport函數

現在假設我們庫的其他部分,也使用了Transport功能,需要比POST更多的HTTP方法。對于它們,我們必須擴展我們的Transport接口:

package monibot

// Transport傳輸請求。
type Transport interface {
    Get(url string) []byte     // 添加,因為health-monitor需要
    Post(url string, body []byte)
    Delete(url string)         // 添加,因為resource-monitor需要
}

現在我們有一個問題。編譯器抱怨我們的fakeTransport不再滿足Transport接口。所以讓我們通過添加缺失的函數來解決它:

// 偽造的Transport是單元測試中使用的Transport。
type fakeTransport struct {
    calls []string
}

func (f *fakeTransport) Get(url string) []byte {
    panic("不使用")
}

func (f *fakeTransport) Post(url string, body []byte) {
    f.calls = append(f.calls, fmt.Sprintf("POST %v, body=%q", url, string(body)))
}

func (f *fakeTransport) Delete(url string) {
    panic("不使用")
}

我們做了什么?由于在單元測試中我們不需要新的Get()和Delete()函數,如果它們被調用,我們就拋出異常。這里有一個問題:每次在Transport中添加新函數時,我們都會破壞現有的fakeTransport實現。對于大型代碼庫來說,這將導致維護噩夢。我們能做得更好嗎?

控制反轉

問題在于我們的客戶端(和相應的單元測試)依賴于一個它們不能控制的類型。在這種情況下,它是Transport接口。為了解決這個問題,讓我們通過引入一個未導出的接口,該接口僅聲明了我們的客戶端所需的內容,來反轉控制:

package monibot

// clientTransport傳輸Client的請求。
type clientTransport interface {
    Post(url string, body []byte)
}

type Client struct {
    transport clientTransport
}

func NewClient(transport clientTransport) *Client {
    return &Client{transport}
}

func (c *Client) PostMetricValue(value int) {
    body := fmt.Sprintf("value=%d", value)
    c.transport.Post("https://monibot.io/api/metric", []byte(body))
}

現在讓我們將我們的單元測試更改為使用假的clientTransport:

// TestPostMetricValue確保客戶端向REST API發送正確的POST請求。
func TestPostMetricValue(t *testing.T) {
    transport := &fakeTransport{}
    client := NewClient(transport)
    client.PostMetricValue(42)
    if len(f.calls) != 1 {
        t.Fatal("期望1次傳輸調用,但是是%d次", len(f.calls))
    }
    if f.calls[0] != "POST https://monibot.io/api/metric, body=\\"value=42\\"" {
        t.Fatal("錯誤的傳輸調用 %q", f.calls[0])
    }
}

// 偽造的Transport是在單元測試中使用的clientTransport。
type fakeTransport struct {
    calls []string
}

func (f *fakeTransport) Post(url string, body []byte) {
    f.calls = append(f.calls, fmt.Sprintf("POST %v, body=%q", url, string(body)))
}

由于Go的隱式接口實現(如果愿意,可以稱之為'鴨子類型'),我們庫的用戶什么也不需要改變:

import "monibot"

func main() {
    // 初始化API客戶端
    var transport monibot.HTTPTransport
    client := monibot.NewClient(transport)
    // 發送指標值
    client.PostMetricValue(42)
}

重新審視Transport

如果我們使IoC成為規范(正如我們應該做的那樣),就不再需要導出Transport接口了。為什么呢?因為如果消費者需要一個接口,讓他們在自己的作用域中定義它,就像我們對'clientTransport'做的那樣。

不要導出接口。導出具體實現。如果消費者需要接口,讓他們在自己的作用域中定義。

總結

在這篇文章中,我展示了如何以及為什么在Go中使用DI和IoC。正確使用DI/IoC可以導致更易于測試和維護的代碼,特別是在代碼庫不斷增長時。雖然代碼示例是用Go編寫的,但這里描述的原則同樣適用于其他編程語言。

責任編輯:武曉燕 來源: 愛發白日夢的后端
相關推薦

2022-04-30 08:50:11

控制反轉Spring依賴注入

2019-09-18 18:12:57

前端javascriptvue.js

2014-01-07 14:39:26

Android開發RxJavaREST

2009-06-12 19:18:08

REST客戶端框架JavaScript

2024-04-01 00:02:56

Go語言代碼

2024-05-27 00:13:27

Go語言框架

2024-07-30 08:12:04

Java消息go

2010-05-31 10:11:32

瘦客戶端

2020-07-14 14:59:00

控制反轉依賴注入容器

2020-11-16 08:05:26

API調用VS Code

2018-12-27 13:11:04

愛奇藝APP優化

2024-04-18 08:39:57

依賴注入控制反轉WPF

2022-09-30 15:31:21

Golang開發工具

2012-12-07 10:15:53

IBMdW

2021-10-18 05:00:38

語言GoRequestHTTP

2021-05-07 15:28:03

Kafka客戶端Sarama

2010-08-31 16:29:40

DHCP客戶端

2011-08-17 10:10:59

2010-12-17 10:16:33

OpenVAS

2021-09-22 15:46:29

虛擬桌面瘦客戶端胖客戶端
點贊
收藏

51CTO技術棧公眾號

影音先锋日韩资源| 亚洲精品国产精品国产自| 你懂的网址一区二区三区| 日本a级不卡| 91精品国产91久久久久| 国产成人午夜性a一级毛片| 制服丝袜激情欧洲亚洲| 小草av在线播放| 中文字幕亚洲在| 日日噜噜噜噜久久久精品毛片| 国产美女娇喘av呻吟久久| 日本视频一区二区不卡| 久久成人国产| 色999日韩自偷自拍美女| 久久精品一区| 亚洲mv在线看| 国产自产v一区二区三区c| 国产成年人在线观看| 韩国成人福利片在线播放| 欧美爱爱视频网站| 国产乱色国产精品免费视频| 男同互操gay射视频在线看| 国产综合成人久久大片91| 26uuu成人| 成人丝袜高跟foot| 情侣黄网站免费看| 国产视频一区在线观看| 亚洲综合色在线观看| 亚洲男人的天堂在线观看| 在线久久视频| 欧美在线观看禁18| 欧洲一区二区三区| 国产桃色电影在线播放| 欧美天天综合色影久久精品| 人妻熟妇乱又伦精品视频| 成人激情在线播放| 欧美大胆视频| 手机福利视频欧美| 国产成人免费电影| 福利二区91精品bt7086| 午夜伦理在线| 日韩一级免费观看| 免费成人黄色| 亚洲激情电影中文字幕| 成人a视频在线| |精品福利一区二区三区| 日韩欧美一区二区视频在线播放| 久久一本综合| 精品免费国产| 国产免费成人| 欧美日韩一区在线播放| 国产精品嫩草99av在线| 亚洲欧美日韩不卡一区二区三区| 美女网站久久| 亚洲小视频在线播放| 麻豆中文一区二区| 天堂在线免费av| 一区二区三区在线高清| 国内一区二区三区在线视频| 伊人久久一区| 精品自在线视频| 一区二区三区在线资源| 性欧美18+| 国产91精品入口| 美女在线视频一区二区 | 欧美区在线播放| 久久国产精品成人免费观看的软件| 亚洲精品欧美在线| 国产精品成人免费精品自在线观看 | 欧美日本一区二区高清播放视频| 中文字幕在线直播| 日韩欧美在线看| 国产精品久久久久久久久免费丝袜 | 国产精品qvod| 欧美激情一区二区久久久| 精品中文在线| 成人黄色片在线| 伊人成人在线视频| 一区二区三区四区欧美| 国精产品一区一区三区mba视频 | www.26天天久久天堂| 2018av男人天堂| 国产97在线亚洲| 欧美午夜片在线观看| 久久国产精品毛片| av第一福利在线导航| 免费看啪啪网站| 欧美高清在线观看| 国产在线一区二区| av影片在线看| 国产精品一区二区三区久久| 91亚洲午夜精品久久久久久| 91破解版在线看| 91精品国产综合久久久久久久久| 中文字幕亚洲一区二区va在线| 麻豆精品国产| 欧美 丝袜 自拍 制服 另类| 精品国模在线视频| 久久精品免费| 日本不卡三区| 国产噜噜噜噜噜久久久久久久久| 欧美国产精品久久| 川上优的av在线一区二区| 亚洲第一精品夜夜躁人人爽| 国产一区二区三区成人欧美日韩在线观看 | 精品国产精品国产偷麻豆| 熟女视频一区二区三区| 欧美巨乳美女视频| 欧美二区乱c少妇| 成人av网站免费观看| 黄色精品一区| 青青在线视频| 国产成人精品免费视| 人人妻人人澡人人爽精品欧美一区| 日韩www在线| 色激情天天射综合网| 一区二区三区四区乱视频| 最大av网站| 午夜在线观看91| 97超碰人人看人人| 亚洲一区二区久久| 亚洲图片欧美色图| 国产精品免费aⅴ片在线观看| 国产精品不卡| 变态调教一区二区三区| 国产一级大片免费看| 欧美激情亚洲自拍| 亚洲国产精品推荐| 日韩av在线播放资源| 国产精品久久久久久久浪潮网站| 国产亚洲电影| 麻豆视频网站在线观看| 欧美精品99久久| 国产精品久久不能| 日韩精品中文字幕在线播放| 99久久国产综合精品色伊 | 蜜桃传媒九九九| 美女av网站| 亚洲成人av在线| 免播放器亚洲| 成人黄色在线电影| 一区二区三区在线观看www| 亚洲国产又黄又爽女人高潮的| 日韩中文字幕区一区有砖一区 | 久久亚洲综合av| 国色天香久久精品国产一区| 成人在线看视频| 国语自产精品视频在线看一大j8 | 在线观看一区二区视频| 欧美一区成人| аⅴ资源新版在线天堂| 国偷自产av一区二区三区小尤奈| 九色精品免费永久在线| 国产三级一区二区三区| 久久久久免费av| 国内黄色精品| 香蕉视频在线免费看| 一二三四中文字幕| 国产精品高清在线| 伊人久久久久久久久久| 黑人巨大精品欧美一区免费视频| 久久国产欧美日韩精品| 亚洲视频精品| 波多野结衣在线观看一区二区| 中文在线中文资源| 丰满诱人av在线播放| 深夜福利视频一区二区| 在线免费看污| 精品久久久久久亚洲| 亚洲午夜免费电影| 最新视频 - x88av| 艳色歌舞团一区二区三区| 国产精品免费久久久| 精品视频偷偷看在线观看| 99久久久久免费精品国产| 亚洲网一区二区三区| 欧美1区3d| 亚洲六月丁香色婷婷综合久久| 水蜜桃亚洲精品| 午夜精品福利一区二区| 鲁丝片一区二区三区| 国产在线精品二区| 麻豆成人在线播放| 99精彩视频在线观看免费| 亚洲一区二区在线| 91久久精品美女高潮| 五月伊人六月| 污片在线免费观看| 亚洲欧洲日本mm| 色先锋久久av资源部| 国产乱人伦精品一区二区| 国产女大学生av| 男人天堂午夜在线| 久久毛片亚洲| 日韩精品午夜| 久久99精品视频| 亚洲欧美成人网| 久久精品99久久久香蕉| 狠狠色狠狠色综合系列| 狠狠做六月爱婷婷综合aⅴ|