開発

2016.06.09

DIとか依存性の注入とか言われても……

どうも、水谷です。
今日は、DIについて、書いてみようと思います。

自分が理解したことを、書いているので、間違いなどあれば、ご指摘いただけると、すごく嬉しいです。

DIとは

Dependency Injectionの略。
日本語訳は、依存性の注入……なんじゃそりゃ??

つまりどういうこと?

クラス間での依存を、外出しするってことらしいです。
そうすることで、メンテナンスとかテストがとっても楽になるそうです。

DI的ではないコードを書く

・例えば、Facebookに「次にお前は????と言う」という内容を投稿するだけのクラス

class YouWillSay {
    public static final String WHO_OTHERS = "他人";
    public static final String WHO_FRIENDS = "友達";
    public static final String WHO_PARENT = "親";

    private FacebookClient fbClient = new FacebookClient(); // (1)

    public void post() {
        // Facebookに投稿してくれる
        fbClient.post(getMessage(WHO_FRIENDS));
    }

    private getMessage(String who) {
        String tmpl = "次にお前は「%d」と、言う!"
        String message = String.format(tmpl, "・・・・");
        switch (who) {
            case WHO_OTHERS:
                message = String.format(tmpl, "やっつけてやるぜ!!");
                break;
            case WHO_FRIENDS:
                message = String.format(tmpl, "ハッピーウレピーよろぴくねー!!");
                break;
            case WHO_PARENT:
                message = String.format(tmpl, "宿題をしなさい!!");
                break;
        }
    }
}
  • (1)で、FacebookClientクラスのインスタンス化しています。
  • こういう時、YouWillSayクラスを実行する時、FacebookClientクラスも正常に動いていないといけない。
    これを「YouWIllSayクラスは、FacebookClientに依存している」と言います。

なにがダメ?

  • YouWIllSayクラスはFacebookClientクラスが正常に動作していないとダメなので、単体テストなどが、FacebookClientクラスが必須になってしまいます。
    テストしている時なども、常にFacebookに投稿されてしまったりと、いろいろと面倒くさいです。
  • FacebookClientクラスがコケると、YouWillSayクラスもコケてしまい、どっちに問題があるのかも分かりづらくなります。

DI的に書く

  • まずは簡単にFacebookClientを外に出す
class YouWillSay {
    ~~ 省略 ~~

    public void post(FacebookClient fbClient) { // (1)
        // Facebookに投稿してくれる
        fbClient.post(getMessage(WHO_CARS));
    }

    private getMessage(String who) {
       ~~ 省略 ~~
    }
}

(1)でインスタンス化したFacebookClientクラスを受け取るようにしました。
しかし、これだとまだFacebookClientが動かないと、やっぱりYouWillSayクラスも動かないので、依存している状態ですよね。

FacebookClientをinterfaceにする

  • FacebookClientをinterfaceとして定義し、実際の実装はそれを継承したクラスに書く。
interface FacebookClient {
   void post(String message);
}
class FacebookClientImpl implements FacebookClient {
    @Override
    public void post(String message) {
        // Facebookに投稿する実装
    }
}

こうすることで、YouWillSayクラスは、FacebookClientクラスには依存させないようにすることができます。
YouWillSayクラスを呼び出すコードを見ながら具体的に説明します。

依存するのはクラスではなく、型

class main {
    YouWillSay yws = new YouWillSay();
    FacebookClient fbClient = new FacebookClientImpl(); // (1)
    yws.post(fbClient);
}

注視すべきは、(1)の部分です、FacebookClientではなく、FacebookClientImplクラスをインスタンス化していますね。
これで、YouWillSayクラスに渡しているのは、FacebookClient型になりました。

なにが違う?

  • FacebookClient型のクラスを渡している
     - 重要なのは、受け取りがクラスではなく、型ということです。
  • つまり、なにも実装していないFacebookClientを継承したTestClientクラスを渡しても、YouWillSayクラスは動く。


    これで、単体テストでは、FacebookClient型のモッククラスを作ってあげればオッケーになりますね。
    つまり、YouWillSayクラスは、FacebookClientクラスには依存しなくなり、外から依存性を注入しているということになります。


    これがDI!でいいのかな・・・?


    以上!
    自分の理解を整理するために書きましたが、誰かの助けになれば幸いです。


    *これらのソースコードは説明用なので、実際に動作は確認していません
Share on FacebookTweet about this on TwitterShare on Google+Share on Tumblr

この記事を書いた人

staff エンジニア:水谷健太
一覧に戻る