DIパターンをInterfaceを使って抽象化して実装する
DIパターン,疎結合とは
DIパターンとは、疎結合な状態のコードを実現するものです。
たとえば以下のような構造体があったとします。
type Team struct{ Members []Member } type Member struct { Name string }
この時にTeamを初期化するような関数を作ったとします。
func NewTeam(names []string) *Team { members := make(Member, len(names)) for i, name := range names { member := &Member{ Name: name, } members[i] = member } return &Team{ Member: members, }
しかし、この関数には大きな問題があります。
それはTeam構造体を初期化したいのに、Member構造体について事前知って、メンバをはじめとするロジックが含まれたコードになっているのです。
このような状態だと、仮にMember構造体を何かしらリファクタリングすると、この関数自体にも影響が出るかもしれません。
このような時に依存関係の解決をします。
以下のようにします。
func main() { members := make(Member, len(names)) for i, name := range names { member := &Member{ Name: name, } members[i] = member } team := NewTeam(members) } func NewTeam(members) []Member) *Team { return &Team{ Member: members, }
のようにできます。また、members
を作るコードもNewMembers
など関数定義をすれば関数ごとに責務をもたらすことができます。
このような状態のことを疎結合の状態と呼びます。
また、今回は初期化のときにMemberを渡していました。 このようなDIの方法をコンストラクタインジェクションとよび、あとからフィールドをセットする方法をプロパティインジェクションと呼ぶ。
たとえば、以下のようなコードでそれは実現できます。
func (t Team) SetMembers(m []Member) {
team.Member = m
}
interfaceを使って抽象化してみる
今回は別のケースです。
データベースにドメインを保存するCreateDomain
関数があるとします。
一般的には構造体ごとにRubyのActiveRecordsのようなsave
関数などを入れると思います。
しかし、そのような「データベースに永続化できる」という性質に着目すればもっとクリーンにかけます。
「データベースに永続化できる」 = storable
という性質を定義します。
type Storable interface{ save() }
そして、このStorable
の性質ももつものを永続化するリポジトリ関数を定義します。
func Create(d Storable) ... { ... }
これによって、このインターフェースを実装しているものなら実行できる抽象的な関数ができました。
特にDIパターンは外部からオブジェクトを渡すので、たくさんあると同じ作業を繰り返してしまいます。
抽象化によってクリーンに書けているのではないかと思います。
テストがしやすい
データベースに保存するようなコードのテストはモックテストで済ませます。
実際にinterface
を使うことで、モックテストをかけるようになっています。