[翻译]在Golang中使用高阶函数实现依赖注入
原文:Dependency injection in Golang using higher order functions
你可以在github.com/steinfletcher/func-dependency-injection-go上找到完整的代码示例。该示例包含一个公开REST端口的http服务。
介绍
在这篇文章中,我们提出了一种在go中依赖注入的方法——使用高阶函数和闭包。
先看看下列返回用户属性的方法。
1 | func GetUserProfile(id string) UserProfile { |
我们期望将处理用户数据的代码和访问数据库的代码分开。在这个例子中,我们希望通过提供对数据库访问的模拟,对主要业务层和其他业务逻辑进行单元测试。接下来分开这些,以至于每个方法都有一个独立的责任。
1 | // 包含业务逻辑或匹配的核心业务层方法代码 |
我们也可以在其他域方法中重复使用方法SelectUserByID
。我们需要一种将SelectUserByID
注入到GetUserProfile
的方法,以至于我们可以在测试中模拟数据访问,进行单元测试GetUserProfile
方法。在Go中一种实现的方法是为函数定义一个类型别名。
类型别名
使GetUserProfile
方法依赖一个抽象,意味着我们可以在测试中注入一个模拟的数据访问层。两种常用的方式分别是使用接口,或类型别名。类型别名很简单,不需要生成一个可以在这儿使用的新结构。接下来为这两个方法都声明别名。
1 | type SelectUserByID func(id string) User |
SelectUserByID
是一个传入用户ID,返回一个User的方法。我们没有定义它的实现。NewGetUserProfile
是一个输入方法selectUser
,然后返回一个可以被调用者使用的方法,的工厂方法。这种策略使用一个闭包,来提供一个内部方法可以被外部方法依赖的入口。闭包可以获得上下文定义的变量和常量。这被称为这些变量和常量的封闭。
可以像下面这样调用这些域函数。
1 | // 在应用中的直接依赖 |
另一种方式
如果你熟悉其他类似Java的语言,这就像创建一个类,注入这个类的依赖到构造函数中,然后通过方法访问依赖。这种访问方式没有方法上的差异——可以将函数别名当作一个只有一个简单抽象方法(SAM)的接口。在Java中,可以使用构造函数来注入依赖。
1 | interface DB { |
而在Go中使用高阶函数实现等价功能的方式如下:
1 | type SelectUser func(id string) User |
测试
现在可以通过模拟数据访问层来单元测试业务层方法。
1 | func TestGetUserProfile(t *testing.T) { |
你可以在github.com/steinfletcher/func-dependency-injection-go找到更完整的代码示例。该示例包含一个公开REST端口的http服务。
译者注:
本文翻译可能不够完美,如有错误请指出,我将改正。