Dipendency Injection(DI)について
詳細については、参考ページを参照するとして、ざっくりしたイメージを思い出せるような備忘録とする。
取り扱うもの
DI(Dipendency Injection)
※ DIコンテナについては、別の備忘録でまとめる予定なので、ここでは扱わない
依存しているとは?
機能Aと機能Bがあり、AはBを呼び出しているとする。 このとき、AはBに依存しているという。
DIとは?
DI(Dipendency Injection)とは、外部からオブジェクトを渡すという考え方・パターンのこと。 プログラミング言語に依らない考え方。
これは、機能Aから機能Bを呼ぶとき、外部からAとBへオブジェクトを渡すことへ置き換えて考える。
1. 外部 → B (外部からBに必要なオブジェクトを渡す) 2. 外部 → A (外部からAにBのオブジェクトを渡す) 3. 項1、2によってAでBを使用できる
ちなみに、Dipendency Injectionは直訳すると「依存注入」だけれど、これは 「依存(されているオブジェクトを)注入(=渡す)」 と捉える。
なぜDI(オブジェクトを渡す)とするのか?
目的は、*コード変更があったとき、その影響範囲を最小限にすること。
機能Aと機能Bが独立していることで、中身が別々に構築でき、ユニットテストも可能となる。
1. AからBを隠蔽する(抽象化する) AがBを呼び出すとき、AがBの内部を考えないようにする。 2. Aのユニットテストを可能とする Bが完成してなくても、Aのみでユニットテストできるようにする。
NGなパターン
AがBを呼び出したいとき、Aでオブジェクトを作成して、Bへ渡してしまうパターン。
1. A → B (Bに必要なオブジェクトをAで作成して渡す)
これだと、AとBを独立させていないので、Aのユニットテストを書けないし、Bが変更されるとAも変更しなければいけない。
OKなパターン(コンストラクタを渡すパターン)
サービス(機能A)からレポジトリ(機能B)を呼び出す構成のとき。
1. DIできるように受け皿をつくっておく * Bにおいて、Bのコンストラクタをつくる * Aにおいて、Aのコンストラクタをつくる 2. DIする(オブジェクトを渡すだけ) * 外部 → B (オブジェクトを渡す) * 外部 → A (オブジェクトを渡す) 3. AでBを使うなどの処理をする
ある「サービス」が「レポジトリ」を利用し、「レポジトリ」が「データベース」を利用する例をコードで確認する。
つまり、依存関係は・・・
- レポジトリ(B)は、データベース(C)に依存している
- サービス(A)は、レポジトリ(B)に依存している
func main() { db := defaultDB() // レポジトリに必要なオブジェクトを渡す repo := NewTicker(db) // サービスに必要なオブジェクトを渡す // サービスはrepoを生成していない service := NewTicker(repo) }
// services // 外部で使えるようインターフェースをつくる type Ticker interface { Create(ticker models.Ticker) error } type ticker struct { repo repositories.Ticker } // コンストラクタ(=New)でオブジェクトを受け取る func NewTicker(repo repositories.Ticker) *ticker { return &ticker{ repo: repo, } } // これはサービスの機能 func (r *ticker) Create(ticker models.Ticker) error { return r.repo.Create(ticker) }
// repositories // serviceで使えるようインターフェースをつくる type Ticker interface { Create(ticker models.Ticker) error } type ticker struct { db *gorm.DB } // コンストラクタ(=New)でオブジェクトを受け取る func NewTicker(db *gorm.DB) *ticker { return &ticker{ db: db, } } // これはリポジトリの機能 func (r *ticker) Create(ticker models.Ticker) error { r.db.Create(ticker) return nil }