1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

DI(依存性注入)を小学生にもわかるように説明する試み

Last updated at Posted at 2021-11-14

DI(依存性注入)を小学生にもわかるように説明する試みです。(タイトルそのまま)
ケーススタディを通じてなぜDIという考え方が便利かを紹介します。

#ケーススタディ①
ログ出力を目的として、ILoggerインターフェースを実装したLoggerAクラスがあるとする。

ILogger.java
public interface ILogger {
    /** ログ出力 */
    public void WriteLog();
}
LoggerA.java
public class LoggerA implements ILogger {

    @Override
    public void WriteLog() {
        // ファイルにログを出力する処理
    }
    
}

あなたはLoggerAクラスを使ってSampleクラスに以下のようなDoSomethingメソッドを作成しました。

Sample.java
public class Sample {
    /** あなたが考えたすごい処理 */
    public void DoSomething() {
        LoggerA loggerA = new LoggerA();
        loggerA.WriteLog();
    }
}

DoSomethingメソッドを作成してしばらく経った頃、あなたの同僚があなたにこう言います。
「君の作ったDoSomethingメソッド、僕も使いたいんだが、一つだけ問題があるんだ。LoggerAクラスをnewしているが、僕の目的を達成するためにはLoggerBクラスをnewしておいてほしかった。」

こんなことを言われたあなたは、怒髪天を衝くぐらい怒り狂って笑顔でDoSomething2メソッドを彼のために作るのでした。

#ケーススタディ②
DB(データベース)にアクセスして、SELECTした結果から計算した結果を返すためにICalculatorインターフェースを実装したCalculatorAが、今回はすでに、用意されているとします。

ICalculator.java
public interface ICalculator {
    /** 計算する処理 */
    public int Calculate();
}
CalculatorA.java
public class CalculatorA implements ICalculator {

    @Override
    public int Calculate() {
        int result = 0;

        // ①DBにアクセスする
        // ②SELECTした結果を計算する

        return result;
    }

}

あなたはCalculatorAクラスを使ったCalculateSomethingメソッドをSampleクラスに作成しました。

Sample.java
public class Sample {
    /** あなたが考えたすごい処理その2 */
    public void CalculateSomething(){
        CalculatorA calculatorA = new CalculatorA();
        int calculatorResult = calculatorA.Calculate();

        // calculatorResultを使ったすごい処理
    }
}

しかし、CalculateSomethingメソッドをテストしようとした時問題が発生しました。

  • CalculatorAメソッドが参照しているDBの内容は日々更新されるので、安定したテストコードが書きにくい
  • CalculatorAメソッドが参照しているDBの内容をテストのために書き換えてはいけない

あなたが書いた素晴らしいロジックには万が一にもバグはないことは自明の理ですが、それでもテストしないわけにはいかないです。でもこのままでは気持ちの良い単体テストが実施できません。

#DI(依存性注入)という考え方
これらのケースで役に立つのがDI(依存性注入)という考え方です。

(あんまり好きではないですが、今回はいい文章が書いてあったので参考にします。)
Wikipediaによると、

dependency injection is a technique in which an object receives other objects that it depends on, called dependencies.

だったり、

The 'injection' refers to the passing of a dependency (a service) into the client that uses it.

であるとのこと。
###つまりDIとは「オブジェクトが依存するオブジェクトを受け取るテクニック」である。「オブジェクトを中でnewするんじゃなくて、newされたオブジェクトを渡してもらいましょう。」っていうこと。

#ケーススタディ①をDIに基づいて書くと

Sample.java
public class Sample {
    private ILogger Logger;

    Sample(ILogger iLogger){
        this.Logger = iLogger;
    }

    /** あなたが考えたすごい処理 */
    public void DoSomething() {
        this.Logger.WriteLog();
    }
}

こう書けば、あなたの素敵な同僚はSampleクラスをnewする際に、コンストラクタの引数にLoggerBクラスを渡せば目的を達成できます(LoggerBのWriteLogメソッドが実行されます)。仮に素敵な同僚がもっと増えてLoggerCLoggerDを使いたいと言い出しても大丈夫そうですね。

#ケーススタディ②をDIに基づいて書くと

Sample.java
public class Sample {
    private ICalculator Calculator;

    Sample(ICalculator calculator) {
        this.Calculator = calculator;
    }

    public void CalculateSomething() {
        int calculatorResult = this.Calculator.Calculate();

        // calculatorResultを使ったすごい処理
    }
}

になります。
テストコードを以下のように書きさえすれば、CalculateSomethingメソッドのテストは大丈夫でしょう。(DBの内容なんて関係なくなるため。)

App.java
public class App {
    public static void main(String[] args) throws Exception {
        Sample sample = new Sample(new TestCalculator());
        sample.CalculateSomething();

        // このあと結果の確認
    }
}

class TestCalculator implements ICalculator {

    @Override
    public int Calculate() {
        // 固定値を返すだけ
        return 57;
    }

}

#まとめ
DIとは「オブジェクトが依存するオブジェクトを受け取るテクニック」である。「オブジェクトを中でnewするんじゃなくて、newされたオブジェクトを渡してもらいましょう。」っていうこと。

#参考文献

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?