Open2

Goのジェネリクスを使ったテクニック

じょうげんじょうげん

Decoratorパターンを構造体のレシーバに適用する方法

GoでDIを行うとき、処理は関数ではなく構造体のレシーバで行うため、Decoratorパターンで処理をラップするには一工夫が必要です。

type Usecase[T any] interface {
  Execute(c context.Context, param T) error
}

func WithEventLog[T any](uc Usecase[T], eventLogParam eventLog.Param) Usecase[T] {
  return usecaseWithLog[T]{uc: uc, eventLogParam: eventLogParam}
}

type usecaseWithLog[T any] struct {
  uc            Usecase[T]
  eventLogParam eventLog.Param
}

func (u usecaseWithLog[T]) Execute(c context.Context, param T) error {
  if err := u.uc.Execute(c, param); err != nil {
    return err
  }
  return eventlog.Send(u.eventLogParam)
}

レシーバは現時点でジェネリクス非対応のため、interface自体の型パラメータを使ってレシーバの多態性を表現する。
また、関数の可変調引数をジェネリクスで表現することはできないので、代わりにstructのフィールドを使って複数の引数を表現する。
これによって任意の数の引数を取るusecaseをジェネリクスを使って表現できるようになる!
他の言語と比べると結構回りくどいけど、実現可能。