Wire中文文档

最佳实践

Preview
  • 最佳实践
  • 区分类型
  • 选项结构体
  • 库中的提供者集
  • 模拟
  • 方法 A:向注入器传递模拟对象
  • 方法 B:从注入器返回模拟对象

最佳实践

以下是我们推荐使用 Wire 的最佳实践。这个列表会不断更新。

区分类型

如果你需要注入一个常见的类型如 string,那么为了避免与其他提供者产生冲突,请创建一个新的字符串类型。例如:

type MySQLConnectionString string

选项结构体

一个包含很多依赖的提供者函数可以与一个选项结构体配对。

type Options struct {
    // Messages 是推荐的问候语集合。
    Messages []Message
    // Writer 是问候消息发送的位置,nil 表示标准输出。
    Writer io.Writer
}

func NewGreeter(ctx context.Context, opts *Options) (*Greeter, error) {
    // ...
}

var GreeterSet = wire.NewSet(wire.Struct(new(Options), "*"), NewGreeter)

库中的提供者集

当为一个库创建提供者集时,唯一可以在不破坏兼容性的情况下进行的更改是:

  • 更改提供者集使用来提供特定输出的提供者,只要不引入新的输入到提供者集中即可。它可以删除输入。但是要注意,现有的注入器将使用旧的提供者,直到它们被重新生成。
  • 将新的输出类型引入到提供者集中,但前提是该类型本身是新添加的。如果该类型不是新类型,则可能某些注入器已经包括了该输出类型,这将导致冲突。

所有其他更改都是不安全的。包括:

  • 在提供者集中要求一个新的输入。
  • 从提供者集中删除一个输出类型。
  • 将现有的输出类型添加到提供者集中。

与其进行这些破坏性的更改,可以考虑添加一个新的提供者集。

例如,如果你有这样一个提供者集:

var GreeterSet = wire.NewSet(NewStdoutGreeter)

func DefaultGreeter(ctx context.Context) *Greeter {
    // ...
}

func NewStdoutGreeter(ctx context.Context, msgs []Message) *Greeter {
    // ...
}

func NewGreeter(ctx context.Context, w io.Writer, msgs []Message) (*Greeter, error) {
    // ...
}

你可以:

  • GreeterSet中使用 DefaultGreeter 而不是 NewStdoutGreeter
  • 创建一个新类型 T,并将 T 的提供者添加到 GreeterSet 中,前提是与提供者一起引入了 T

你不能:

  • GreeterSet 中使用 NewGreeter 而不是 NewStdoutGreeter。这将添加一个输入类型(io.Writer),并要求注入器在 *Greeter 的提供者之前返回一个 error,这要求不同于之前。
  • GreeterSet 中删除 NewStdoutGreeter。依赖于 *Greeter 的注入器将被破坏。
  • GreeterSet 中添加一个 io.Writer 的提供者。注入器可能已经为 io.Writer 有一个提供者,这可能会造成冲突。

因此,你应该仔细选择库提供者集中的输出类型。通常情况下,应该优先选择较小的库提供者集。例如,一个库提供者集通常只包含一个单独的提供者函数和 wire.Bind 语句,绑定返回类型所实现的接口。避免使用较大的提供者集可以减少应用程序遇到冲突的可能性。比如,假设你的库提供了一个用于 Web 服务的客户端,尽管在库的提供者集中绑定 *http.Client 看似很有用,但是如果每个库都做同样的事情,会造成冲突。相反,库提供者集应该只包括 API 客户端的提供者,并让 *http.Client 成为提供者集的输入。

模拟

有两种方法可以创建具有模拟依赖项的注入应用程序。这些方法的示例详见 此处

方法 A:向注入器传递模拟对象

创建一个仅用于测试的注入器,将所有模拟对象作为参数传入。参数类型必须是模拟对象所模拟的接口类型。无法在 wire.Build 中包含用于模拟依赖项的提供者而不会产生冲突,因此,如果你使用的是提供者集,则需要定义一个不包括模拟类型的提供者集。

方法 B:从注入器返回模拟对象

创建一个新的结构,其中包含应用程序以及希望模拟的所有依赖项。创建一个仅用于测试的注入器,返回此结构,在其中为具体类型的模拟对象提供提供者,并使用 wire.Bind 告诉 Wire 应该使用具体类型的模拟对象来实现相应的接口。