golang在使用wire时注入多个接口的实现的方法

1,212 阅读1分钟

背景:假设我有个系统可以发送短信和邮箱的时候 使用wire应该怎么注入呢?

定义一个发送接口

type IHandler interface {
   Name() string
   Do(ctx context.Context) error
}

然后分别实现短信和邮箱的发送逻辑

// Sms 发送短信
type Sms struct {
}

func NewSms() IHandler {
   return &Sms{}
}

func (s Sms) Name() string {
   return "sms"
}

func (s Sms) Do(ctx context.Context) error {
   fmt.Println("do sms")
   return nil
}

// Email 发送邮箱
type Email struct {
}

func NewEmail() IHandler {
   return &Email{}
}

func (s Email) Name() string {
   return "email"
}

func (s Email) Do(ctx context.Context) error {
   fmt.Println("do email")
   return nil
}

然后注入到wire

var ProviderSet = wire.NewSet(
   NewSms,
   NewEmail,
)
func wireApp() *App {
   panic(wire.Build(
      ProviderSet,
      newApp,
   ))
}

执行wire 我们发现报错了提示我们不能重复一个接口注入多个实现

image.png

那么我们正确的应该怎么做呢?

加入一个Manager结构体来管理我们的多个实现

type Manager struct {
   hmap map[string]IHandler
}

func NewManager(
   sms *Sms,
   email *Email,
) *Manager {
   hmap := make(map[string]IHandler)
   hmap[sms.Name()] = sms
   hmap[email.Name()] = email
   return &Manager{hmap: hmap}
}
func (m Manager) Get(name string) (IHandler, error) {
   if v, ok := m.hmap[name]; ok {
      return v, nil
   }
   return nil, errors.New("unknown handle")
}

然后修改原来的短信sms和邮箱email的实现返回具体实现而不是返回接口


// Sms 发送短信
type Sms struct {
}

func NewSms() *Sms {
   return &Sms{}
}

func (s Sms) Name() string {
   return "sms"
}

func (s Sms) Do(ctx context.Context) error {
   fmt.Println("do sms")
   return nil
}

// Email 发送邮箱
type Email struct {
}

func NewEmail() *Email {
   return &Email{}
}

func (s Email) Name() string {
   return "email"
}

func (s Email) Do(ctx context.Context) error {
   fmt.Println("do email")
   return nil
}

然后注入到wire

var ProviderSet = wire.NewSet(
   NewSms,
   NewEmail,
   NewManager,
)
func wireApp() *App {
   panic(wire.Build(
      ProviderSet,
      newApp,
   ))
}

生成的wire_gen.go

// Code generated by Wire. DO NOT EDIT.

//go:generate go run github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinject

package main

// Injectors from wire.go:

func wireApp() *App {
   sms := NewSms()
   email := NewEmail()
   manager := NewManager(sms, email)
   app := newApp(manager)
   return app
}

我们发现执行正常了

image.png

写个方法测试一下

func main() {
   app := wireApp()
   ctx := context.Background()
   var h IHandler
   h, _ = app.manager.Get("sms")
   fmt.Println(h.Name())
   h.Do(ctx)
   h, _ = app.manager.Get("email")
   fmt.Println(h.Name())
   h.Do(ctx)
}

image.png

这样我们增加发送方法就只需要在NewManager往后增加就行了