コンテナとDI
前節でログの書き出しを紹介しました。ここで、コンテナの話に戻ります。先のログは実運用では必須の機能なので、アプリのさまざまな箇所にログへの書き出しコードを埋めていきます。特に例外処理でのcatchブロックなどでは必須といえます。
ところが、これは、あくまで実運用での話であり、テスト段階、特に個々のメソッドが想定どおりに動作するかをチェックする単体テスト(ユニットテスト)では、ログへの書き出し機能は不要となります。むしろ、ログファイルが肥大化し、そちらのメンテナンスが大変になります。そういった場合に都合のいい仕組みが、このコンテナなのです。
例えば、リスト4の段階で以下のようにLoggerをnewする処理を記述したとします。
$logger = new Logger("firstslim");
こうすると、このルーティングコールバック関数は、この時点でMonologのLoggerクラスに依存することになり、$loggerは他でもないMonologのLoggerインスタンスそのものを表します。これは、別にLoggerクラスに限った話ではなく、Twigクラスでもそうです。コード中であるクラスをnewする処理を記述すると、その時点でそのコードはnewしたクラスに依存することになります。
ところが、これを、リスト4のようにコンテナからもらうようにするとどうなるでしょう。
$logger = $this->get("logger");
この記述では、コンテナへの登録処理コードがしょせんLoggerクラスをnewしてリターンしているというのを知っているだけに、確かに$loggerはMonologのLoggerインスタンスを表すと思えます。しかし、ここでもし、コンテナへの登録処理のコードを以下のように変更したとします。
$container["logger"] = function($container)
{
$logger = new DummyLogger();
return $logger;
};
ここで登場したDummyLoggerクラスは、MonologのLoggerクラスに似せて作った(同じメソッドを記述した)、何も処理を行わないクラスとします。そして、index.phpに記述したコンテナへの登録処理を丸々上のコードに差し替えたとしたら、このアプリ中のルーティングコールバック関数内で使われる、以下のコードの$loggerは途端にDummyLoggerを表すことになります。
$logger = $this->get("logger");
つまり、$loggerという変数のクラスへの依存関係を変数宣言の時に確定させるのではなく、コンテナからもらうことで、実行時までずらすことができるのです。
このように、クラスの依存関係を実行時までずらすことを、「依存性注入」といい、英語では「Dependency Injection(DI)」といいます。まさに、依存関係を実行時に注入する仕組みです。そして、それを実現するのが、このコンテナなのです。
そのため実は、このコンテナのことを正式には「Dependency Container(依存性コンテナ)」、あるいは、「Dependency Injection Container(依存性注入コンテナ)」といいます。
このDIのメリットは、先に解説したように、実運用環境とテスト環境といった、環境が違う場合に利用するクラスを、実運用のものとダミーなものに切り替える、といったことが容易にできることです。特に、各メソッドのテストを行うユニットテストを行おうとすると、このDI技術は必須といえます。
まとめ
今回は、Slimにおけるコンテナの使い方を扱いました。そのコンテナを使ってのログの扱い方、さらには、なぜコンテナが必要かというところでDIを紹介しました。
次回は、これまでルーティングコールバック関数内に記述していた処理を1つのクラスにまとめることができる方法として、コントローラクラスを扱います。
