内容简介:Go团队最近公布了用于开放云开发的可移植云API和工具,开源项目依赖注入是一种编写可伸缩、低耦合代码的标准技术。因为依赖注入显式地为组件提供他们需要工作的所有依赖关系。 在Go中,这通常采用将依赖项传递给构造函数的形式:这种技术在小规模下工作得很好,但是较大的应用程序会存在一个复杂的依赖图。这导致了一大块依赖于顺序的初始化代码,这并不好玩。 因为一些依赖项被多次使用,通常很难干净地拆分这些代码。 将服务的一个实现替换为另一个实现也会很痛苦,因为这涉及到通过添加一组全新的依赖项(及其依赖项...)来修改依赖
Go团队最近公布了用于开放云开发的可移植云API和工具,开源项目 Go Cloud 。 这篇文章详细介绍了Wire,一个随Go Cloud提供的依赖注入工具。
Wire解决了什么问题?
依赖注入是一种编写可伸缩、低耦合代码的标准技术。因为依赖注入显式地为组件提供他们需要工作的所有依赖关系。 在 Go 中,这通常采用将依赖项传递给构造函数的形式:
// NewUserStore返回一个使用cfg和db作为依赖项的UserStore。
func NewUserStore(cfg *Config, db *mysql.DB) (*UserStore, error) {...}
复制代码
这种技术在小规模下工作得很好,但是较大的应用程序会存在一个复杂的依赖图。这导致了一大块依赖于顺序的初始化代码,这并不好玩。 因为一些依赖项被多次使用,通常很难干净地拆分这些代码。 将服务的一个实现替换为另一个实现也会很痛苦,因为这涉及到通过添加一组全新的依赖项(及其依赖项...)来修改依赖项图,并删除未使用的旧项。 实际上,在具有庞大依赖图的应用程序中更改初始化代码是繁琐且缓慢的。
像Wire这样的依赖注入 工具 旨在简化初始化代码的管理。您可以将您的服务及其依赖关系描述为代码或配置,然后Wire处理生成关系图,再据此确定初始化 排序 以及如何向每个服务传递它所需的依赖。 通过更改函数签名、添加或删除初始化程序来更改应用程序的依赖项,然后让Wire执行为整个依赖图生成初始化代码的繁琐工作。
为什么这是Go Cloud的一部分?
Go Cloud的目标是通过为合适的云服务提供惯用的Go API,使编写便携式云应用程序变得更加容易。 例如, blob.Bucket
提供了一个存储API,其中包含亚马逊S3和谷歌云存储(GCS)的实现; 使用 blob.Bucket
编写的应用程序可以交换实现而无需更改其应用程序逻辑。 但是,初始化代码本质上是特定于提供者的,并且每个提供者具有不同的依赖集。
例如,
构建GCS blob.Bucket
需要 gcp.HTTPClient
,最终需要 google.Credentials
,而 为S3构建一个
则需要 aws.Config
,最终需要AWS凭据。 因此,更新应用程序以使用不同的 blob.Bucket
实现涉及到我们上面描述的依赖关系图的那种繁琐的更新。 Wire的驱动用例是为了方便交换Go Cloud可移植API的实现,但同时它也是依赖注入的通用工具。
这些工作不是已经做过了吗?
确实有许多依赖注入框架。对Go来说, Uber的dig 和 Facebook的inject 都使用反射来进行运行时依赖注入。 Wire的主要灵感来自 Java 的Dagger 2 ,并且使用代码生成而不是反射或服务定位器 。
我们认为这种方法有几个优点:
- 当依赖关系图变得复杂时,运行时依赖注入很难跟踪和调试。 使用代码生成意味着在运行时执行的初始化代码是常规的,惯用的Go代码,易于理解和调试。不会因为框架的各种奇技淫巧而变得生涩难懂。特别重要的是,忘记依赖项等问题会成为编译时错误,而不是运行时错误。
- 与服务定位器不同,不需要费心编造名称来注册服务。 Wire使用Go语法中的类型将组件与其依赖项连接起来。
- 更容易防止依赖项变得臃肿。 Wire生成的代码只会导入您需要的依赖项,因此您的二进制文件将不会有未使用的导入。 运行时依赖性注入器在运行之前无法识别未使用的依赖项。
- Wire的依赖图是静态可知的,这为工具化和可视化提供了可能。
它是如何工作的?
Wire有两个基本概念:提供者和注射器。
提供者 是普通的Go函数,它们根据它们的依赖关系“提供”值,这些值被简单地描述为函数的参数。 以下是一些定义三个提供程序的示例代码:
// NewUserStore与我们上面看到的功能相同; 它是UserStore的提供者,
//依赖于*Config和*mysql.DB。
func NewUserStore(cfg *Config, db *mysql.DB) (*UserStore, error) {...}
// NewDefaultConfig是*Config的提供者,没有依赖。
func NewDefaultConfig() *Config {...}
// NewDB是基于某些连接信息的* mysql.DB的提供者。
func NewDB(info *ConnectionInfo) (*mysql.DB, error) {...}
复制代码
通常一起使用的 ProviderSets
可以分组到 ProviderSets
。 例如,在创建 *UserStore
时使用默认的 *Config
是很常见的,因此我们可以在 ProviderSet
中对 NewUserStore
和 NewDefaultConfig
进行分组:
var UserStoreSet = wire.ProviderSet(NewUserStore, NewDefaultConfig) 复制代码
注入器
是被生成的函数,它们按依赖所需的顺序调用提供者。 编写注入器的签名,包括任何所需的输入作为参数,并插入对 wire.Build
的调用, wire.Build
包含构造最终结果所需的提供者或提供者集的列表:
func initUserStore() (*UserStore, error) {
//我们将得到一个错误,因为NewDB需要一个*ConnectionInfo
//我们没有提供。
wire.Build(UserStoreSet, NewDB)
return nil, nil // 这些返回值会被忽略。
}
复制代码
现在我们运行go generate来执行wire:
$ go generate wire.go:2:10: inject initUserStore: no provider found for ConnectionInfo (required by provider of *mysql.DB) wire: generate failed 复制代码
哎呀! 我们没有包含 ConnectionInfo
也没有告诉Wire如何构建一个。 Wire有用地告诉我们涉及的行号和类型。 我们可以将它的提供者添加到 wire.Build
,或者将其添加为参数:
func initUserStore(info ConnectionInfo) (*UserStore, error) {
wire.Build(UserStoreSet, NewDB)
return nil, nil // 这些返回值会被忽略。
}
复制代码
现在 go generate
将使用生成的代码创建一个新文件:
// File: wire_gen.go
// Code generated by Wire. DO NOT EDIT.
//go:generate wire
//+build !wireinject
func initUserStore(info ConnectionInfo)(* UserStore,error){
defaultConfig:= NewDefaultConfig()
db,err:= NewDB(info)
if err!= nil {
return nil, err
}
userStore,err:= NewUserStore(defaultConfig,db)
if err!= nil {
return nil, err
}
return userStore,nil
}
复制代码
任何非注入器声明都将复制到生成的文件中。 在运行时没有依赖Wire:所有编写的代码都是正常的Go代码。
如您所见,输出非常接近开发人员自己编写的内容。 这只是一个简单的例子,只有三个组件,因此手工编写初始化程序并不会太痛苦,但Wire为具有更复杂依赖关系图的组件和应用程序节省了大量的手工操作。
我如何参与并了解更多信息?
Wire README 详细介绍了如何使用Wire及其更高级的功能。 还有一个 教程 可以在一个简单的应用程序中使用Wire。
感谢您对Wire使用体验的任何意见! Go Cloud的 开发是在GitHub上进行的,所以你可以 提出一个问题 来告诉我们什么可能更好。 有关项目的更新和讨论,请加入项目的邮件列表 。
感谢您抽出宝贵时间了解Go Cloud的Wire。 我们很高兴与您合作,使Go成为构建可移植云应用程序的开发人员的首选语言。
作者:Robert van Gent
原文:blog.golang.org/wire
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Learn Python the Hard Way
Zed A. Shaw / Addison-Wesley Professional / 2013-10-11 / USD 39.99
Master Python and become a programmer-even if you never thought you could! This breakthrough book and CD can help practically anyone get started in programming. It's called "The Hard Way," but it's re......一起来看看 《Learn Python the Hard Way》 这本书的介绍吧!