我正在开发一个小型 Go 应用程序,它基本上是各种密码存储(Ansible Vault、Hashicorp Vault、Chef Vault 等)的包装器。这个想法是:在我的各种配置脚本中,我可以使用我的 Go 包装器来获取 secret ,如果我们决定在幕后切换密码存储,则不需要在我的项目中更新所有接口(interface)。
我正在尝试为此应用程序设置适当的测试,并在此过程中尝试找出注入(inject)依赖项的最佳方法。
例如,假设该项目名为 secrets
。我的实现之一是 ansible。 ansible 实现需要自己的解析器
,并且需要打开自己的连接
到 ansibleVault,以检索数据。
所以我可能有以下内容:
package secrets
type PasswordStore interface {
GetKey(key string) (string, error)
}
func New(backend string, config map[string]interface{}) (PasswordStore, error) {
switch backend {
case "ansible":
return ansible.New(config)
default:
return nil, fmt.Errorf("Password store '%s' not supported.", backend)
}
}
package ansible
type Connection interface {
open() (string, error)
}
type Ansible struct {
connection Connection
contents map[string]string
}
func New(c map[string]interface{}) (*Ansible, error) {
conn, err := NewConnection(c["ansible_path"].(string))
if err != nil {
return nil, err
}
// open connection, parse, etc...
a := &Ansible{
connection: conn,
contents: parsedData,
}
return a, nil
}
所以这看起来不错,因为 secrets
包不需要了解 ansible
包依赖项(连接),并且工厂刚刚使用一些配置新建了实例数据。但是,如果我需要模拟 Ansible 接收的 connection
,似乎没有一个好的方法来做到这一点(除非该配置映射有一个名为 mock
的连接选项>)
另一个选择是放弃工厂,只组装 secrets
包中的所有依赖项,例如:
package secrets
type PasswordStore interface {
GetKey(key string) (string, error)
}
func New(backend string, config map[string]interface{}) (PasswordStore, error) {
switch backend {
case "ansible":
return ansible.New(AnsibleConnection{}, config)
default:
return nil, fmt.Errorf("Password store '%s' not supported.", backend)
}
}
package ansible
// same as before in this file, but with injected dependency ...
func New(connect Connection, c map[string]interface{}) (*Ansible, error) {
conn, err := connect.NewConnection(c["ansible_path"].(string))
if err != nil {
return nil, err
}
// open connection, parse, etc...
a := &Ansible{
connection: conn,
contents: parsedData,
}
return a, nil
}
现在依赖项已注入(inject),但似乎 secrets
需要了解每个实现的每个依赖项。
是否有更合乎逻辑的方式来构建这个,以便 secrets
知道得更少?或者顶层包通常会协调一切?
最佳答案
什么决定了后端是什么?这应该有助于指导你。我在一个项目中做过类似的支持多个数据库的事情,我所做的基本上是:
config
包读取配置文件,该文件确定正在使用的后端store
包提供通用接口(interface),并具有一个接受配置并返回实现的函数server
包仅引用接口(interface)main
包读取配置,将其传递给store
中的工厂函数,然后在创建时将结果注入(inject)到服务器
因此,当我创建服务器(实际上使用数据存储)时,我将配置传递给 store
中的工厂函数,该函数返回一个接口(interface),然后将其注入(inject)到服务器中。关于不同的具体实现,唯一需要了解的是公开接口(interface)和工厂的同一个包; server
、config
和 main
包将其视为黑匣子。
关于go - 依赖注入(inject)和测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45764315/