💉

Dependency InjectionをTypescriptで解説

2022/08/20に公開

DI(依存性の注入)とは

あるクラス(User)の中で別のクラス(Dynamo)のインスタンスを利用する場合

class User{
    saveUser(id: number, name: string) {
        const db = new Dynamo()
        db.saveUser(id, name)
    }
}


interface DB {
    saveUser(id: number, name: string): string
}

class Dynamo implements DB{
    saveUser(id: number, name: string){
        const res = this._dynamoDbClient(id, name)
        return res
    }
    _dynamoDbClient(id: number, name: string){
        return 'Successfully Saved!!'
    }
}

const user = new User()
user.saveUser(1, 'たくろう')

このように書くことがあるだろう.
このようにUserクラス内でDynamoクラスのインスタンスを作るのではなく
コンストラクタ、プロパティ、メソッド経由でDynamoインスタンスを渡す事でUserクラスのDynamoクラスへの依存性を注入することをDIと呼ぶ

DIの種類

Dependency Injectionの実装方法には3種類ある

  • フィールドインジェクション
  • セッターインジェクション
  • コンストラクタインジェクション

この中でも, コンストラクタインジェクションが一番いいと思う
コンストラクタインジェクションはコンストラクタ経由の注入という意味でインスタンス作成時に依存側のクラスが必要とするオブジェクトを全部コンストラクタに渡さなければならない.
なので,どのクラスを使っているかという依存関係を把握しやすかったりうっかり渡し忘れるということが起こらないという点で一番いいんじゃない?と思う.今のところ

これをDIを用いて実装するとどうなるだろう

//Userクラスはコンストラクタを通してdbクラスを受け取っている
class User {
    db: DB
    constructor(db: DB){
        this.db = db
    }
//saveUserメソットではコンストラクタで受け取ったDBクラスのメソッドを呼び出しているだけ
	saveUser(id: number, name: string){
        this.db.saveUser(id, name)
	}
}

interface DB {
    saveUser(id: number, name: string): string
}

class Dynamo implements DB{
    saveUser(id: number, name: string){
        const res = this._dynamoDbClient(id, name)
        return res
    }

    _dynamoDbClient(id: number, name: string){
        return 'Successfully Saved!!'
    }
}

// Userクラスに注入するDynamokクラスは自由に付け替えることが可能
const user = new User(new Dynamo())
user.saveUser(2, 'たくろう')

Userクラスに対してDynamoクラスを注入しているのがわかるだろう.

何が嬉しいの?

DIを導入することで得られる恩恵は幾つがあるが

  • クラス間の依存関係を少なくし疎結合に
  • 変更に強い(例えばdynamoDBじゃなくてRDBなどに変更になっても新しくRDB用のクラスを作成してUserクラスに注入すればいい)
  • テストがしやすい(これも結局は変更に強いと近しいことだが依存するクラスをMockオブジェクトに置き換えるだけで単体テストが容易になる)

最近DIという概念に触れたので頭の整理のためにまとめた

Discussion