サービスコンテナについて学習したので備忘録
サービスコンテナとは
The Laravel service container is a powerful tool for managing class dependencies and performing dependency injection. Dependency injection is a fancy phrase that essentially means this: class dependencies are "injected" into the class via the constructor or, in some cases, "setter" methods.
(Laravel公式ドキュメント より引用)
サービスコンテナは、
- クラスの依存関係を管理する機能を持つ
- あるクラスを、依存関係を持たせたいクラスに注入する機能(依存性注入)を提供する機能を持つ
さらに要約すると、
「クラスの依存関係と、インスタンス作成を管理してくれる機能を持つ」
といった感じです。
用語
まず前提知識として、以下の用語について説明します。
正直なところ、前提知識の理解ができていれば、サービスコンテナの理解はそこまで難しくないと思います。
・依存関係
既に何度か使っている依存関係についてです。
例えばクラスA内のコードで、それとは別のクラスBのインスタンスを作成していると、
「クラスAはクラスBに依存している」
と言います。
これらの関係を 依存関係 と言います。
・依存性注入 / Dependency injection (DI)
Laravelの公式ドキュメントにも登場しましたが、依存性注入についてです。
依存性注入とは、コンピュータプログラムのデザインパターンの一つで、
オブジェクトなどの間に生じる依存関係をオブジェクト内のコードに直接記述せず、外部から何らかの形で与えるようにする手法。
(IT用語辞典 より引用)
つまりは、
「コンストラクタ等を使って外部から、
あるオブジェクトに、別のオブジェクトを渡すこと」
を言うみたいです。
依存性注入は直訳ですが、以下の記事を見ると、
「依存性」=「オブジェクト」
だということが分かります。
元の英語版のwikipediaにはこう書いてありました。
A dependency is an object that can be used (a service).
「Dependency」(依存性と訳していた)は、「オブジェクト」です。
つまり、DIとは、「依存性の注入」じゃなくて、「オブジェクトの注入」だった訳ですね。
(DI・DIコンテナ、ちゃんと理解出来てる・・?より引用)
この依存性注入(DI)についての理解は、サービスコンテナを理解する上で最も重要になるので、
分からなければ、さらに自分で調べてみてください。
・依存解決 / resolve
The "resolve" function resolves a given class or interface name to an instance using the service container
(Laravel公式ドキュメント より引用)
これはヘルパ関数のresolve()についての説明なのですが、
「サービスコンテナから依存関係をたどってインスタンスを取り出すこと」
を指すようです。
どちらかといえば、「依存取得」「依存解析」とかの方が分かりやすいのではと思ったり、、、
日本語ドキュメント(Readouble)だと、依存解決と訳しているみたいでした。
主な機能
・設定なしの依存解決
<?php
use App\Services\Service; //ルート定義
class SampleController
{
public function __construct(Service $service) //コンストラクタの引数に型宣言
{
//
}
}
別に作成されたServiceクラスをルート定義し、
SampleControllerクラスのコンストラクタの引数に、タイプヒンティング(型宣言)することで、
Serviceクラスを依存解決することができます。
どういうことかと言うと、
本来ならば
<?php
class SampleController
{
public function __construct()
{
$service = new Service()
//
}
}
というように、newを使ってインスタンスを生成します。
ですがサービスコンテナがあれば、
自動的にインスタンスを取得(依存解決)してくれるので、
この作業が要らないというわけです。
ただし、重要なところはそこじゃありません。
サービスコンテナは、コンストラクタの引数として”外部から”インスタンスを渡している、
依存性注入(DI) を行っています。
普通にインスタンスを作成するのと何が違うかは後述します。
・設定ありの依存解決
ほとんどの場合、設定なしで依存解決を行えますが、
以下の場合、設定ありで依存解決を行う必要があります。
- インターフェースを実装したクラスを依存解決したいとき
- 他のLaravel開発者に共有する予定のLaravelパッケージを作成しているとき
では実際にどのように設定するか説明します。
結合 / bind
結合はサービスコンテナに、キーの値とどのクラスを呼び出すかを定義し、組み込むことです。
結合をしておくことで、キー値を使えば指定したクラスのインスタンスを呼び出すことができます。
サービスコンテナの結合は、サービスプロバイダーに記述することで、登録されます。
・シンプルな結合
サービスプロバイダ内では、「$this->app」でコンテナに接続でき、
「bind」メソッドを使うことで、結合を登録することができます。
$this->app->bind(登録するクラスやインターフェース, 呼び出すクロージャ)
以下は、ServiceMakerクラスを依存解決する際は、
Serviceクラスのインスタンスを引数に渡してインスタンスを生成すると定義しています。
「make」メソッドは、依存解決を行ってくれるメソッドです。
use App\MyClasses\ServiceMaker
use App\MyClasses\Service
$this->app->bind(ServiceMaker::class, function($app) {
return new ServiceMaker($app->make(Service::class));
})
・インタフェースを実装したクラスの結合
SampleInterfaceを実装したServiceクラスを、インタフェースと結合する際は、以下のように省略して書けます。
app()->bind(SampleInterface::class, Service::class);
この結合を登録しておけば、以下のように
インタフェースをタイプヒンティング(型宣言)でServiceクラスを呼び出すことができます。
use App\Interface\SampleInterface
public function __construct(SampleInterface $sample){
//
}
・その他
今回は割愛しますが、
ひとつのインスタンスを全機能で使いまわす(シングルトン)実装を簡単にする結合 があったり、
クラスによって依存注入するクラスを場合分けする結合 があったりします。
興味のある方はご自分で調べてみてください。
メリット
では、そもそも何のためにあるのかという話です。
まずここまで何度も出てきたDIのメリットについてですが、
- 外部から注入したクラスに変更があっても、呼び出し元(サービスコンテナの結合の登録)を変更するだけで済む
- ユニットテストを行う際に、クラスのインスタンスを外部から呼び出しているため、ダミーやモックに容易に変更できる
(ピンとこない方は、こちらの記事が分かりやすかったです。)
つまりサービスコンテナは、多くの機能で
メリットがある依存性注入(DI)を簡単に実装できること
が大きなメリットではないかと思います。
まとめ
Laravelのサービスコンテナについて書きました。
難しい内容だったので、まとめきれてないかもしれないので、見つけ次第教えてくださると幸いです。