Laravelで "メソッドインジェクション" と "コンストラクタインジェクション" を体験してみた件
メソッドインジェクションやコンストラクタインジェクションについて詳しく書かれている記事は結構見かけるので、
この記事ではソースコードを見て端的にメソッドインジェクションとコンストラクタインジェクションの
概要をLaravelの内部動作関係なしに理解できるように、この記事を書きました。
<?php
namespace App\Test;
interface interface_basketballplayer
{
public function shot();
}
このインターフェースを実装したものがlebronクラス
<?
namespace App\Test;
use App\Test\interface_basketballplayer;
class lebron implements interface_basketballplayer
{
public function shot()
{
return "レブロンのダンクシュート";
}
}
このクラスのインスタンスをコントローラーのアクションで使いたい時、以下のようにするだけで使うことができる。
use App\Test\lebron;
public function test(lebron $lebron)
{
echo $lebron->shot();
}
//ブラウザに"レブロンのダンクシュート"と表示される
なぜかインスタンス化しなくても、メソッドの引数にクラスとそのインスタンスが代入される変数を指定しているだけなのに、lebronクラスのインスタンスを使うことができています。
これがメソッドインジェクションです。
主にサービスコンテナが仕事をしてくれています。
次に考える例は引数に指定したクラスがコンストラクタを持っていた場合です。
例えば以下のようなクラスがあった場合
<?php
namespace App\Test;
class StarPlayer
{
private $player;
// public function __construct(interface_basketballplayer $player)
// {
// $this->player = $player;
// }
public function __construct(lebron $player)
{
$this->player = $player;
}
public function play()
{
echo $this->player->shot();
}
}
このクラスをインスタンス化するには、lebronインスタンスを渡さなくてはいけません。
では先ほどと同様にコントローラーの引数にて、StarPlayerを引数に指定するとどうなるでしょうか。
use App\Test\StarPlayer;
public function test2(StarPlayer $starPlayer)
{
$starPlayer->play();
}
//ブラウザに"レブロンのダンクシュート"と表示される
先ほどの例と違う点はコンストラクタが指定されおり、そのコンストラクタにlebronクラスのインスタンスを渡さなければいけない点です。
アクションの引数にクラスのインスタンスを指定するだけでインスタンス化できる点は前例と同じですが、
インスタンス化する際にコンストラクタに指定されたlebronクラスのインスタンスが自動で渡されていることが分かります。
これがコンストラクタインジェクションです。
こちらに関してもメソッドインジェクション同様にサービスコンテナが働いています。
先ほどコンストラクタの引数にインターフェースを指定したらどうなるのでしょう。
<?php
namespace App\Test;
class StarPlayer
{
private $player;
public function __construct(interface_basketballplayer $player)
{
$this->player = $player;
}
// public function __construct(lebron $player)
// {
// $this->player = $player;
// }
public function play()
{
echo $this->player->shot();
}
}
結論、このままではサービスコンテナは引数にどんなインスタンスを渡せばいいか分かりませんのでエラーが起こります。
インターフェースをインスタンス化することはできないので。
なのでサービスコンテナにこのインターフェースが指定されたら、このクラスのインスタンスを渡してと
教えてあげる必要があります。サービスプロバイダーで。
$this->app->bind(\App\Test\interface_basketballplayer::class,\App\Test\lebron::class);
コンストラクタの引数にinterface_basketballplayerのインスタンスが指定されていたら、
lebronクラスのインスタンスを渡してあげてとサービスプロバイダーで設定します。するとエラーが消えます。
Discussion