自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

依賴注入與控制反轉(zhuǎn):優(yōu)化Go語言REST API客戶端

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

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

一個簡單的客戶端實現(xiàn)

我們從一個簡單的客戶端實現(xiàn)開始,允許調(diào)用者訪問Monibot的REST API,具體來說,是為了發(fā)送指標值??蛻舳说膶崿F(xiàn)可能如下所示:

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()
    // 發(fā)送指標值
    client.PostMetricValue(42)
}

依賴注入

現(xiàn)在假設(shè)我們想對客戶端進行單元測試。當所有HTTP發(fā)送代碼都是硬編碼的時候,我們?nèi)绾螠y試客戶端呢?對于每次測試運行,我們都需要一個“真實”的HTTP服務(wù)器來回答我們發(fā)送給它的所有請求。不可?。∥覀兛梢宰龅酶茫鹤屛覀儗TTP處理作為“依賴”;讓我們發(fā)明一個 Transport 接口:

package monibot

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

讓我們再發(fā)明一個具體的使用HTTP作為通信協(xié)議的Transport:

package monibot

// HTTPTransport是一個使用HTTP協(xié)議傳輸請求的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))
}

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

import "monibot"

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

單元測試

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

// TestPostMetricValue確??蛻舳讼騌EST API發(fā)送正確的POST請求。
func TestPostMetricValue(t *testing.T) {
    transport := &fakeTransport{}
    client := NewClient(transport)
    client.PostMetricValue(42)
    if len(transport.calls) != 1 {
        t.Fatal("期望1次傳輸調(diào)用,但是是%d次", len(transport.calls))
    }
    if transport.calls[0] != "POST https://monibot.io/api/metric, body=\\"value=42\\"" {
        t.Fatal("錯誤的傳輸調(diào)用 %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函數(shù)

現(xiàn)在假設(shè)我們庫的其他部分,也使用了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需要
}

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

// 偽造的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()函數(shù),如果它們被調(diào)用,我們就拋出異常。這里有一個問題:每次在Transport中添加新函數(shù)時,我們都會破壞現(xiàn)有的fakeTransport實現(xiàn)。對于大型代碼庫來說,這將導致維護噩夢。我們能做得更好嗎?

控制反轉(zhuǎn)

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

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))
}

現(xiàn)在讓我們將我們的單元測試更改為使用假的clientTransport:

// TestPostMetricValue確??蛻舳讼騌EST API發(fā)送正確的POST請求。
func TestPostMetricValue(t *testing.T) {
    transport := &fakeTransport{}
    client := NewClient(transport)
    client.PostMetricValue(42)
    if len(f.calls) != 1 {
        t.Fatal("期望1次傳輸調(diào)用,但是是%d次", len(f.calls))
    }
    if f.calls[0] != "POST https://monibot.io/api/metric, body=\\"value=42\\"" {
        t.Fatal("錯誤的傳輸調(diào)用 %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的隱式接口實現(xiàn)(如果愿意,可以稱之為'鴨子類型'),我們庫的用戶什么也不需要改變:

import "monibot"

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

重新審視Transport

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

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

總結(jié)

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

責任編輯:武曉燕 來源: 愛發(fā)白日夢的后端
相關(guān)推薦

2019-09-18 18:12:57

前端javascriptvue.js

2022-04-30 08:50:11

控制反轉(zhuǎn)Spring依賴注入

2014-01-07 14:39:26

Android開發(fā)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

2020-07-14 14:59:00

控制反轉(zhuǎn)依賴注入容器

2020-11-16 08:05:26

API調(diào)用VS Code

2010-05-31 10:11:32

瘦客戶端

2018-12-27 13:11:04

愛奇藝APP優(yōu)化

2024-04-18 08:39:57

依賴注入控制反轉(zhuǎn)WPF

2022-09-30 15:31:21

Golang開發(fā)工具

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客戶端

2010-12-17 10:16:33

OpenVAS

2011-08-17 10:10:59

2021-09-22 15:46:29

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

51CTO技術(shù)棧公眾號