どうも、水谷です。
今日は、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!でいいのかな・・・?
以上!
自分の理解を整理するために書きましたが、誰かの助けになれば幸いです。
*これらのソースコードは説明用なので、実際に動作は確認していません