当前位置:首页 > 科技  > 软件

依赖注入与控制反转:优化Go语言REST API客户端

来源: 责编: 时间:2023-12-09 15:21:28 410观看
导读在这篇文章中,我将探讨依赖注入(DI)和控制反转(IoC)是什么,以及它们的重要性。作为示例,我将使用Monibot的REST API客户端。让我们开始吧:一个简单的客户端实现我们从一个简单的客户端实现开始,允许调用者访问Monibot的REST AP

在这篇文章中,我将探讨依赖注入(DI)和控制反转(IoC)是什么,以及它们的重要性。作为示例,我将使用Monibot的REST API客户端。让我们开始吧:ul028资讯网——每日最新资讯28at.com

一个简单的客户端实现

我们从一个简单的客户端实现开始,允许调用者访问Monibot的REST API,具体来说,是为了发送指标值。客户端的实现可能如下所示:ul028资讯网——每日最新资讯28at.com

package monibottype 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。我们的库的用户可能像这样使用它:ul028资讯网——每日最新资讯28at.com

import "monibot"func main() {    // 初始化API客户端    client := monibot.NewClient()    // 发送指标值    client.PostMetricValue(42)}

依赖注入

现在假设我们想对客户端进行单元测试。当所有HTTP发送代码都是硬编码的时候,我们如何测试客户端呢?对于每次测试运行,我们都需要一个“真实”的HTTP服务器来回答我们发送给它的所有请求。不可取!我们可以做得更好:让我们将HTTP处理作为“依赖”;让我们发明一个 Transport 接口:ul028资讯网——每日最新资讯28at.com

package monibot// Transport传输请求。type Transport interface {    Post(url string, body []byte)}

让我们再发明一个具体的使用HTTP作为通信协议的Transport:ul028资讯网——每日最新资讯28at.com

package monibot// HTTPTransport是一个使用HTTP协议传输请求的Transport。type HTTPTransport struct {}func (t HTTPTransport) Post(url string, data []byte) {    http.Post(url, data)}

然后让我们重写客户端,使其“依赖”于一个Transport 接口:ul028资讯网——每日最新资讯28at.com

package monibottype 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(客户端的依赖项)被“注入”到客户端中。调用者可以这样初始化一个客户端:ul028资讯网——每日最新资讯28at.com

import "monibot"func main() {    // 初始化API客户端    var transport monibot.HTTPTransport    client := monibot.NewClient(transport)    // 发送指标值    client.PostMetricValue(42)}

单元测试

现在我们可以编写一个使用“伪造”Transport的单元测试:ul028资讯网——每日最新资讯28at.com

// 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接口:ul028资讯网——每日最新资讯28at.com

package monibot// Transport传输请求。type Transport interface {    Get(url string) []byte     // 添加,因为health-monitor需要    Post(url string, body []byte)    Delete(url string)         // 添加,因为resource-monitor需要}

现在我们有一个问题。编译器抱怨我们的fakeTransport不再满足Transport接口。所以让我们通过添加缺失的函数来解决它:ul028资讯网——每日最新资讯28at.com

// 伪造的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实现。对于大型代码库来说,这将导致维护噩梦。我们能做得更好吗?ul028资讯网——每日最新资讯28at.com

控制反转

问题在于我们的客户端(和相应的单元测试)依赖于一个它们不能控制的类型。在这种情况下,它是Transport接口。为了解决这个问题,让我们通过引入一个未导出的接口,该接口仅声明了我们的客户端所需的内容,来反转控制:ul028资讯网——每日最新资讯28at.com

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:ul028资讯网——每日最新资讯28at.com

// 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的隐式接口实现(如果愿意,可以称之为'鸭子类型'),我们库的用户什么也不需要改变:ul028资讯网——每日最新资讯28at.com

import "monibot"func main() {    // 初始化API客户端    var transport monibot.HTTPTransport    client := monibot.NewClient(transport)    // 发送指标值    client.PostMetricValue(42)}

重新审视Transport

如果我们使IoC成为规范(正如我们应该做的那样),就不再需要导出Transport接口了。为什么呢?因为如果消费者需要一个接口,让他们在自己的作用域中定义它,就像我们对'clientTransport'做的那样。ul028资讯网——每日最新资讯28at.com

不要导出接口。导出具体实现。如果消费者需要接口,让他们在自己的作用域中定义。ul028资讯网——每日最新资讯28at.com

总结

在这篇文章中,我展示了如何以及为什么在Go中使用DI和IoC。正确使用DI/IoC可以导致更易于测试和维护的代码,特别是在代码库不断增长时。虽然代码示例是用Go编写的,但这里描述的原则同样适用于其他编程语言。ul028资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-40651-0.html依赖注入与控制反转:优化Go语言REST API客户端

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 一致性哈希:数据分片与负载均衡的黄金法则

下一篇: 微软:VS Code已成为Java巨头!

标签:
  • 热门焦点
  • K60至尊版狂暴引擎2.0加持:超177万跑分斩获性能第一

    Redmi的后性能时代战略发布会今天下午如期举办,在本次发布会上,Redmi公布了多项关于和联发科的深度合作,以及新机K60 Ultra在软件和硬件方面的特性,例如:“K60 至尊版,双芯旗舰
  • 服务存储设计模式:Cache-Aside模式

    Cache-Aside模式一种常用的缓存方式,通常是把数据从主存储加载到KV缓存中,加速后续的访问。在存在重复度的场景,Cache-Aside可以提升服务性能,降低底层存储的压力,缺点是缓存和底
  • 一文掌握 Golang 模糊测试(Fuzz Testing)

    模糊测试(Fuzz Testing)模糊测试(Fuzz Testing)是通过向目标系统提供非预期的输入并监视异常结果来发现软件漏洞的方法。可以用来发现应用程序、操作系统和网络协议等中的漏洞或
  • 共享单车的故事讲到哪了?

    来源丨海克财经与共享充电宝相差不多,共享单车已很久没有被国内热点新闻关照到了。除了一再涨价和用户直呼用不起了。近日多家媒体再发报道称,成都、天津、郑州等地多个共享单
  • 中国家电海外掘金正当时|出海专题

    作者|吴南南编辑|胡展嘉运营|陈佳慧出品|零态LT(ID:LingTai_LT)2023年,出海市场战况空前,中国创业者在海外纷纷摩拳擦掌,以期能够把中国的商业模式、创业理念、战略打法输出海外,他们依
  • 腾讯VS网易,最卷游戏暑期档,谁能笑到最后?

    作者:无锈钵来源:财经无忌7月16日晚,上海1862时尚艺术中心。伴随着幻象的精准命中,硕大的荧幕之上,比分被定格在了14:12,被寄予厚望的EDG战队以绝对的优势战胜了BLG战队,拿下了总决
  • 认真聊聊东方甄选:如何告别低垂的果实

    来源:山核桃作者:财经无忌爆火一年后,俞敏洪和他的东方甄选依旧是颇受外界关心的“网红”。7月5日至9日,为期5天的东方甄选“甘肃行”首次在自有App内直播,
  • iQOO Neo8系列今日官宣:首发天玑9200+ 全球安卓最强芯!

    在昨日举行的的联发科新一代旗舰芯片天玑9200+的发布会上,iQOO官方也正式宣布,全新的iQOO Neo8系列新品将全球首发搭载这款当前性能最强大的移动平台
  • iQOO Neo8 Pro真机谍照曝光:天玑9200+和V1+旗舰双芯加持

    去年10月,iQOO推出了iQOO Neo7系列机型,不仅搭载了天玑9000+,而且是同价位唯一一款天玑9000+直屏旗舰,一经上市便受到了用户的广泛关注。在时隔半年后,
Top