依赖反转 依赖注入_在go中使用依赖反转

依赖反转 依赖注入

什么是依赖倒置? (What is Dependency Inversion?)

Dependency Inversion is the idea that high-level logic should not depend on its low-level implementations. Business logic in our application should not care about whether we get data from an AWS bucket or Google Cloud Storage; we should be able to easily swap these implementations without our program breaking. This makes our code stable against change. We can also make our application testable by swapping these dependencies for implementations that are easier to test.

依赖倒置是高级逻辑不应依赖于其低级实现的想法。 应用程序中的业务逻辑不应在乎我们是从AWS存储桶中获取数据还是从Google Cloud Storage中获取数据; 我们应该能够在不中断程序的情况下轻松地交换这些实现。 这使我们的代码对更改保持稳定。 通过将这些依赖项交换为易于测试的实现,我们还可以使我们的应用程序可测试。

How is this done in Go?

在Go中如何完成?

In Go, interfaces enable us to use dependency inversion. We are able to use different implementations in our code, as long as they satisfy the interface we have defined. We use dependency injection to tell the application which implementation to use.

在Go中,接口使我们能够使用依赖反转。 只要它们满足我们定义的接口,我们就可以在代码中使用不同的实现。 我们使用依赖注入来告诉应用程序使用哪个实现。

工作实例 (Worked Example)

To demonstrate how this works in Go we’re going to build upon a Quote API that provides random quotes to our users. Below is a screenshot of our current Go handler that provides Kanye West quotes.

为了演示这在Go中的工作原理,我们将基于Quote API来为用户提供随机报价。 下面是我们当前的Go处理程序的屏幕快照,其中提供了Kanye West的报价。

This handler does a perfectly good job of giving a user Kanye West quotes; it’s what you might expect when you first write a HTTP handler in Go. It makes a HTTP call to https://api.kanye.rest which provides the quote and checks the response is valid. When you call the handler, you will receive a response with a quote:

这个处理程序在为用户Kanye West报价时做得非常好。 这就是您第一次在Go中编写HTTP处理程序时可能会遇到的情况。 它对https://api.kanye.rest进行HTTP调用,以提供引号并检查响应是否有效。 调用处理程序时,您将收到带有引号的响应:

Image for post

We would however like to start testing the handler but currently have no way of doing that without making actual HTTP calls. We also have no way of testing what happens when our dependency returns a bad response. This is where we can start using dependency inversion.

但是,我们希望开始测试处理程序,但是如果不进行实际的HTTP调用,目前无法做到这一点。 我们也无法测试当我们的依赖返回错误响应时会发生什么。 这是我们可以开始使用依赖倒置的地方。

We first need to create an interface, this will define the behaviour of our dependency. By doing this we will later be able to swap in different implementations as long as they share the same behaviour. This is our interface:

我们首先需要创建一个接口,这将定义我们依赖项的行为。 这样,只要它们共享相同的行为,我们以后便可以交换不同的实现。 这是我们的界面:

Image for post

Any struct that has a method called ‘Get’ that takes a string as an argument and returns a pointer to a response type from the net/http package with an error, will fulfil the Client interface. This is the behaviour we are interested in.

任何具有名为“ Get”的方法的结构(将字符串作为参数,并从net / http包中返回带有错误的响应类型的指针)都将实现Client接口。 这是我们感兴趣的行为。

Now we need a way of injecting the dependency into the handler to make it agnostic to whatever implementation of the Client interface we choose to use. There are many approaches of dependency injection in Go. I will outline a few below:

现在,我们需要一种将依赖项注入到处理程序中的方法,以使其与我们选择使用的Client接口的任何实现无关。 Go中有很多依赖注入方法。 我将在下面概述一些:

高阶函数 (Higher Order Function)

We could create a higher-order function that returned our original handler function. This is convenient as you only need to call the higher order function with what you need to use, we also do not need to create a handler struct.

我们可以创建一个返回原始处理程序函数的高阶函数。 这很方便,因为您只需要使用需要使用的函数调用高阶函数,我们也不需要创建处理程序结构。

Image for post

Our HTTP dependency is then injected from our main function by calling the higher-order function.

然后,通过调用高阶函数从我们的主函数注入HTTP依赖项。

Image for post

建设者 (Constructor)

We can create a ‘handlers’ struct, which has a constructor function where we supply the client implementation. The handlers struct has a field called client which is where our implementation is stored.

我们可以创建一个“ handlers”结构,该结构具有一个构造函数,用于提供客户端实现。 该处理程序结构具有一个名为client的字段,该字段存储我们的实现。

Image for post

By making the Kanye function a handlers receiver method, we can access the client field.

通过使Kanye函数成为处理程序接收器方法,我们可以访问client字段。

Image for post

We then inject the HTTP client from the main function by constructing the handlers struct with the correct dependencies.

然后,通过使用正确的依赖项构造处理程序结构,从主要功能注入HTTP客户端。

A disadvantage of this approach is that, when used a lot it can make you construct a lot of structs in your main file.

这种方法的一个缺点是,当大量使用它时,可能会使您在主文件中构造很多结构。

Image for post

使用选项 (Using Options)

Another approach is to use options in our handlers constructor. In this approach we set a default implementation to use unless the user gives an alternative as an option. The handlers constructor would be a variadic function, so that either options can be given to it or not. In the example below we export a higher order option function WithCustomClient, to make it easy for the user to use an alternative Client implementation.

另一种方法是在处理程序构造函数中使用选项。 在这种方法中,除非用户提供替代选项,否则我们将使用默认实现。 处理程序的构造函数将是一个可变参数函数,因此可以为它提供或不提供选项。 在下面的示例中,我们导出了具有更高顺序的选项函数WithCustomClient ,以使用户可以轻松使用替代的Client实现。

Image for post

Using the constructor we can now use the default implementation (http.DefaultClient) by calling NewHandlers without any arguments:

使用构造我们现在可以通过调用不带任何参数NewHandlers使用默认的实现(http.DefaultClient):

Image for post

Or we can call it with a custom Client option to use a non-default:

或者我们可以使用自定义Client选项调用它以使用非默认值:

Image for post

Using this approach, when you want to use a default implementation, you do not need to construct those implementations yourself which can leave your main file less convoluted.

使用这种方法,当您要使用默认实现时,不需要自己构造那些可以使主文件减少混乱的实现。

测试实施 (Test Implementation)

Now we have covered the different dependency injection approaches, we will create an implementation of the Client interface to be used in test. From here we will use higher order functions to inject dependency.

现在我们已经介绍了不同的依赖注入方法,我们将创建要在测试中使用的Client接口的实现。 从这里开始,我们将使用更高阶的函数来注入依赖关系。

At this point we could either write a test double ourselves or use GoMock to generate an implementation that allows us to control its behaviour though stubbing. For this example, we will use GoMock.

在这一点上,我们可以自己编写一个测试,也可以使用GoMock生成一个实现,该实现允许我们通过存根控制其行为。 在此示例中,我们将使用GoMock。

By adding the line of code below into our code base we can automatically generate a test double from the Client interface.

通过将下面的代码行添加到我们的代码库中,我们可以自动从Client界面生成一个测试双。

Image for post

By running go generate ./… we generate the following code:

通过运行go generate./…,我们生成以下代码:

Image for post

This is an implantation of Client we can use in tests. We can inject this, instead of the default HTTP client and mock the behaviour of making real HTTP Get calls to the Kanye API. We are now able to write tests without making HTTP calls and test what happens when we get a bad response.

这是我们可以在测试中使用的Client的植入。 我们可以注入它,而不是默认的HTTP客户端,并模拟对Kanye API进行真正的HTTP Get调用的行为。 现在,我们无需编写HTTP调用就可以编写测试,并测试当收到错误响应时会发生什么。

Below is an example of the type of test case we can now write. The test uses Ginkgo, a Behaviour Driven Development Testing Framework.

下面是我们现在可以编写的测试用例类型的示例。 该测试使用行为驱动的开发测试框架Ginkgo。

Image for post

The test case stubs the mock client implementation. We check it is called with the correct URL string and tell it to return our HTTP response without an error. We can check that the HTTP status code and the quote sent to the user are as we expect.

测试用例对模拟客户端实现进行存根。 我们检查是否使用正确的URL字符串调用了它,并告诉它返回我们的HTTP响应而没有错误。 我们可以检查HTTP状态代码和发送给用户的报价是否符合我们的预期。

结论 (Conclusion)

We have covered how to use dependency inversion to alter our handler so that it can be used with any implementation of Client. This technique can be applied to any type of dependency.

我们已经介绍了如何使用依赖倒置来更改我们的处理程序,以便它可以与任何Client实现一起使用。 该技术可以应用于任何类型的依赖项。

Dependency Inversion can be a powerful tool to create programs that are more stable and robust as features are added to it. We can create tests that mock dependencies and by depending on a concept rather than an implementation, we reduce the amount of change to business logic.

依赖倒置可以是一个功能强大的工具,它可以创建添加了功能的程序,从而使其更加稳定和健壮。 我们可以创建模拟依赖关系的测试,并通过依赖概念而非实现来减少对业务逻辑的更改。

If you want to read more about dependency inversion, I highly recommend Clean Architecture by Robert C Martin. https://www.goodreads.com/book/show/18043011-clean-architecture

如果您想了解有关依赖倒置的更多信息,我强烈推荐Robert C Martin撰写的Clean Architecture。 https://www.goodreads.com/book/show/18043011-clean-architecture

All the code examples can be found here:

所有代码示例都可以在这里找到:

https://github.com/cobbinma/quotes

https://github.com/cobbinma/quotes

翻译自: https://medium.com/@cobbinma/using-dependency-inversion-in-go-31d8bf9b3760

依赖反转 依赖注入

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值