DIとはなんぞや?

f:id:hira98:20190524210744p:plain

【結論】

  • DIとはpendencyInjection(依存性の注入)の略称であり、オブジェクト指向プログラミングにおける開発手法の1つである。
  • SpringにおけるDIを理解するには、GoF提唱のデザインパターンの1つであるFactoryMethodパターンを理解する必要がある。
  • DIは、Rubyアプリ開発する際に使用する、gemとbundlerの関係に置き換えることができる。

【目次】

はじめに

Java開発で使用するフレームワークデファクトスタンダードであるSpring。

こいつについて学習していると、SpringはDIとAOPなる2つ概念がベースにあることが分かりました。

SpringはDI(Dependency Injection、依存性の注入)という概念をベースにし、更にAOP(Aspect Oriented Programming、アスペクト指向プログラミング)という概念を実行することで、Springは「さまざな分野における高度なフレームワークを実装する際の基盤」として機能するようになってききます。

引用元:『EclipseではじめるJavaフレームワーク入門 第5版』

使うフレームワークのベースになる概念も理解しないで、フレームワークを使いこなせるわけながない。

という、謎の強迫観念に駆られたので、AOPは置いておいて、まずはDIについて調べてみることにしました。

DIってなんの略?

DIとはDpendencyInjection(依存性の注入)の略称です。

依存性ってなんだよ??

と思っていたら、いい記事を見つけました。

元の英語版のwikipediaにはこう書いてありました。

A dependency is an object that can be used (a service).

Dependency」(依存性と訳していた)は、「オブジェクト」です。

つまり、DIとは、「依存性の注入」じゃなくて、「オブジェクトの注入」だった訳ですね。

引用:DI・DIコンテナ、ちゃんと理解出来てる・・?

オブジェクトの方が依存性なんかよりも、分かりやすいです。

Javaでいうと、オブジェクトはクラス変数をnew()して作られるやつですからね。

そいつを注入するんですから、ソースにすると以下の感じですかね?

Integer num = new Integer();

…で?

結局DIってなに?

って感じですね。。

DIを使う利点は?

DIという単語の持つ意味については理解できました。

しかし、DIという概念も、概念を用いて実装することによって得られる利点もよく分かりません。

若干調べるのが面倒くさくなってきた僕です。

お馬さんが目の前に人参をぶら下げると元気よく走り出すように、僕も理解することで楽できると知れば頑張って勉強します。

自分を奮い立たせるために、まずは利点を調べてみました。

どうやら、DIを発展させたDIコンテナは次のようなことをやってくれようです。

1) インスタンスの生成

DIコンテナの中で、インスタンスを生成します。つまり、クラスをnewします。そして、アプリケーションではそれらのインスタンスを取得して利用します。DIの中で、毎回newしたインスタンスをアプリケーションに渡すのか、それとも、一度newしたインスタンスをアプリケーションに渡すのかを管理してくれます。

2) インスタンスのライフサイクル管理を簡単に実装できるようになります。

インスタンスの生成とライフサイクル管理(インスタンスの破棄)をDIがやってくれるため、クラスをnewしたり、使い終わった変数にnullを入れる必要がなくなりました。

引用:Spring解体新書

これは便利です。俄然やる気が出ます。

だって、newしなくていいんですよ。

メモリ管理から解放されるんですよ。

メモリ確保できてるか?メモリ解放漏れはないか?を気にしなくていいとか。

DIサイコーかよ。

DIを理解するにあたって必要な知識

『Spring解体新書』によるとDIがDpendencyInjectionの略である以上、Dpendency(依存性)とInjection(注入)について理解する必要があるようです。

依存性とは?

『Spring解体新書』には次のように書かれていました。

・他のクラスをローカル変数として持っている

・他のクラスがメソッドの引数、戻り値になっている

引用:Spring解体新書

これだけ引用しても、何を言っているか分からないと思います。

引用した本では、車に搭載するエンジンを変更する例をサンプルソースも提示しながら説明していて、とても分かりやすかったです。

理解した内容を、車を例に説明すると劣化コピーか無断転載になってしまいます。

で、どうまとめるべきか考えた結果、出た答えがRuby会議に参加した時に知ったエコシステムという単語です。

エコシステムの詳しい概念は知らないですが、ITに関して具体的なエコシステムは以下があると理解しています。

要するに、他の人が作ったプログラムを使ったり、自分が作ったプログラムを他の人に提供したりを簡単にできるようにするためのシステムをエコシステムというのだと認識しています。

正直、エコシステムがなくても他の人が作ったプログラムを使うことはできます。

使って欲しい人はネットにソースを公開し、使いたい人はネットからソースを拾ってきてコンパイル→実行すればいいだけです。

ただし、コンパイルしたライブラリのバージョン管理や、実行ファイルの保存場所などの情報を管理する必要があり、かなり面倒です。

しかも、各個人で好き勝手やっているので、同じライブラリを使っていたとしても、PC環境によって使い方が異なります。

このような状態だと、PC環境が異なればライブラリの使い方も異なるため、ライブラリの使い方はPC環境に依存している状態と言えます。

f:id:hira98:20190526033138p:plain

PC環境に依存せずにライブラリを使うために用意されているのがエコシステムです。

Rubyではgemがあるおかげで、gemの仕組みを使ってライブラリをインストールしているPC環境においては、共通の操作でライブラリを使えるようになります。 つまり、gemというインタフェースを介してPC環境のライブラリを操作することによって、ライブラリの使い方はPC環境に依存しなくなったと言い換えることができます。

f:id:hira98:20190526033154p:plain

注入とは?

『Spring解体新書』には次のように書かれていました。

変数にインタンスを入れることを注入と言います。

引用:Spring解体新書

依存性の説明と同じく、サンプルソース使って分かりやすく説明しくれてました。

が、劣化コピーにならないように、私は依存性で用いた、rubyとgemの関係を延長して説明していきます。

gemを使えば、PC環境に依存せずにライブラリを使えることを説明しました。

しかし、これだけだと色々なgemを使って開発する上ではまだ役不足なんです。

一人であれば特に問題ありません。必要に応じて開発環境にライブラリを追加していけばいいだけです。

ですが、チームで『某フリマアプリのクローンサイト』を開発するケースだと色々不都合が出てきます。

どんな、gemを入れたかをチーム内で共有する必要があるのです。

Aさんがdotenvというgemを追加した場合、他のチームメンバーもdotenvというgemを追加する必要があります。gem数個だったらまだいいですが、これが数十とか数百になるともうパニックです。

なんで、Rubyではgemを管理するためのgemであるbundlerというものが用意されています。

gemを追加・削除したい時は、bundlerが管理するファイルを変更しチーム内で共有します。チームメンバーはbundlerを介してgemを更新するだけで済みます。

bundlerを使う利点は、dotenv、turbolinks、devise、…etcどんなgemを追加したとしても、チームメンバーはgemに依存する操作は不要で、bundler操作だけを行えば良いという点です。

これらをJavaソースに置き換えると以下のように表せる(はず💦)です。

(public つけてるclassやinterfaceでファイル分ける必要あると思うんですが、手間なのでひとまとめにしています。)

//bundlerを使わない場合
public interface Gem{} //gemインタフェース
public class Dotenv_v1 implements Gem{} //gemクラス

public class MyPC {
    static void main(String[] args) {
    Gem gem = new Dotenv_v1 //PC環境へのgemインストール操作
  }
}
//bundlerを使う場合
public interface Gem{} //gemインタフェース                  
public class Dotenv_v1 implements Gem{} //gemクラス
public class Dotenv_v2 implements Gem{} //gemクラス

//bundlerクラス
public class Bundler {
  static getDotenv() {
    //インストールしたいgemのバージョンを指定する。
    return new Dotenv_v1(); //バージョン変更時はここだけ変更すれば良い。
  }
}

public class MyPC {
    static void main(String[] args) {
        Gem gem = Bundler.getDotenv(); //PC環境へのgemインストール操作
  }
}

注目すべきは、bundlerを使うことによって、Dotenvのバージョをv1からv2に変えてもMyPCクラスの処理を変える必要がないことです。

getDotenv()のように、クラスをnewしてインスタンスを返すメソッドをFactoryメソッドと言います。『Spring解体新書』には次のようにまとめられていました。

Factoryメソッドを使うことで、newするクラスが変わった場合、メソッドの中身だけを変えるだけで済むようになります。つまり、変更に強いアプリケーションとなります。

引用:Spring解体新書

参考情報

Spring解体新書

→Springについて解説されたkindle本。STS3ベースなので使えない情報もありまが、DIの説明はとても分かりやかったです。

https://ryoutaku-jo.hatenablog.com/

→ブログ投稿のモチベーションを維持するための情報源です。

最後に

技術ブログを書いているつもりなんですが、技術本を紹介するためのアフィリエイト記事を書いている気がしてなりません。 もっと、詳しい情報知りたい人は「この本ポチってねー」的な。。