ひがやすを技術ブログ

電通国際情報サービスのプログラマ

DIって本当に必要?

DIって本当に必要?たまにそう思うときがあります。DIによって開発は本当に楽になったのか。
DIのメリットでよく語られることとして、インターフェースと実装を分離し、機能の利用者側はインターフェースを通じて機能を利用することで、実装に直接依存しなくなり、後で実装を変更しても影響を受けなくなるということがあります。
実際後から、実装クラスを変更するということはめったにないので、よくあるのは、テストのために実装クラスをモックに変えることです。
でも、別にそれだけならDIContainerなんていりません。たとえば、次のようにServiceクラスに直接依存したClientがあるとします。


class Client {
Service service = new Service();
void setService(Service service) {
this.service = service;
}
void go() {
service.go();
}
}
class Service {
void go() {
...
}
}
このClientクラスをモックを使ってテストするのも簡単です。

class ServiceMock extends Service {
void go() {
//テスト用の処理
}
}
class ClientTest extends TestCase {
void testGo() {
Client client = new Client();
client.setService(new ServiceMock());
...
}
}
Serviceクラスのメソッド間の呼び出しに副作用がなければ、別にいちいちインターフェースを用意することもDIContainerを使う必要もないのです。
それでは、Serviceクラスのgoメソッドの実装に時間がかかりそうな場合で、すぐにClientをテストしたい場合はどうでしょうか。やっぱりインターフェースが必要でしょうか。
そのような場合は、goメソッドが完成するまで、goメソッドをabstractにしておけばよいのです。あるいは、//TODOを入れておいて何も処理しないようにしておき、後で実装を追加してもよいかもしれません。
フレームワークのように後から実装を丸ごと置き換えるようなことがある場合は、インターフェースにしておいたほうが、継承元の実装の影響を受けないので望ましいと思いますが、業務アプリケーションの開発ではそんなケースはほとんどないでしょう。
実は業務アプリケーションではインターフェースなんていらないかもしれないというのが最近の私の仮説。
単に実装を置き換えやすくするだけなら、setterメソッドを用意しておけばよいだけのこと。これ(setterメソッド)をDIと呼ぶのなら、DIは必要だと思いますが、だったらDIContainerなんて大げさなものはいらないですよね。
DIContainerは実は不要なのでしょうか。
私は、DIContainerが提供するある2つの機能はとても重要でそのためにDIContainerの存在価値はまだあると思っています。
その2つの機能が提供できるなら、現在のDIContainerの形をとっていなくてもよいと思っています。
その2つの機能とは何か、DIContainerの存在価値を考える意味でよいポイントだと思うので、一度考えてみると面白いのではないかと思います。
今の私の考えでは、DIそのものはsetterを用意しておけば済む話だと思うので、あまり重要だとは思っていません。
外から依存性を解決するから、実装の変更に強いというのは、最初の例で説明したようにあまり重要なポイントではないと思っています。
DIは2つの機能には含まれていないということです。
それでは続きはまた明日。
追記:ServiceMockがServiceをextendsし忘れていたのを修正。