コンストラクタインジェクションとは
依存先のモジュールを自ら生成せずに、コンストラクタの引数としてもらうようにすることです。
はい。イメージつきにくいですよね...。
コンストラクタインジェクションしていない例
Carクラス内で、Engineクラスのインスタンスを作成しています。
class Car
{
private $engine;
public function __construct()
{
$this->engine = new Engine();
}
public function move()
{
$energy = $this->engine->run();
}
}
コンストラクタインジェクションしている例
Carクラス内で、Engineクラスのインスタンスは作成していません。
引数としてEngineクラスのインスタンスを受け取っています。
class Car
{
private $engine;
public function __construct(Engine $engine)
{
$this->engine = $engine;
}
public function move()
{
$energy = $this->engine->run();
}
}
メリット
1, 疎結合になる
依存モジュール同士が疎結合になることで依存先の具体的な実装の変更に依存元のコードは影響を受けなくなります。
先ほどの例で考えてみます。普通のエンジンではなく加速がすごいスーパーエンジンを使いたいとします。その場合carクラスのインスタンス作成時に引数でスーパーエンジンを渡してあげればいいだけです。Carクラスの変更は無しで済みます。
public function hoge()
{
superEngine = new superEngine();
car = new Car(superEngine);
}
2, テスト容易性が向上する
エンジンをモック化が必要であれば、モックしたものをCarクラスに引数として渡してあげればいいだけです。
public function testMove()
{
engineMock = new engineMock();
car = new Car(engineMock);
Car.move();
}
Carクラスの引数がエンジンだけでなく複数の引数を取るとします。
ある引数はモックに、他の引数はモックではなく本物を渡すみたいなこともできます。
そして、何より見やすいし理解しやすいと思います。
public function testMove()
{
engineMock = new engineMock();
wheel = new wheel();
car = new Car(engineMock, wheel);
Car.move();
}
コンストラクタインジェクションをしとけば完璧なのか?
完璧ではないと思います。
例えば、コンストラクタの引数が増えたら、毎回インスタンスを作成するたびに、引数の管理が大変ですよね。
public function hoge()
{
classA = new classA();
classB = new classB();
classC = new classC();
classD = new classD();
...
car = new Car(classA, classB, classC, classD...);
....
}
そこで登場するのが、DIコンテナ(サービスコンテナ) です。
ここではDIコンテナの説明をすると話が拡がってしまうので、一旦ここまでにします。
まとめ
コンストラクタインジェクションによって、疎結合とテスト容易性が向上できます。
疎結合で保守性の高いコードにしていきましょう。