Laravel DI 調べてみた
DIとは
DI(依存性の注入)とは、クラスやメソッド内で使用する機能を外部(具体的にいうと仮引数)から渡す設計パターンです。
機能を外部から渡すことによって、クラスやメソッド同士の依存性を小さくする目的があります。
具体例
今回は、ラジオ番組コントローラー内でコーナを所得する例を考えてみたいと思います。
以下は、ラジオ番組クラスとラジオコントローラーになります。
Annkw.php
<?php
namespace App\Radios;
class Annkw
{
private $name;
private $corner;
public function __construct($name = 'annkw', $corner='しんやめ')
{
$this->name = $name;
$this->corner = $corner;
}
public function getName()
{
return $this->name;
}
public function getCorner()
{
return $this->corner;
}
}
RadioController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Radios\Annkw;
class RadioController extends Controller
{
public function corner(): string
{
$annkw = New Annkw();
return $annkw->getCorner();
}
}
RadioController.php内のcornerメソッドでは、Annkwクラスをインスタンス化することによって実装しています。
つまり、cornerメソッドを使用するにはAnnkwクラスが必要である=Annkwクラスに依存しているということになります。
そこで、DIのパターンを使用することができます。
DIの実装パターンは、
①コンストラクタインジェクション
②メソッドインジェクション
の2つになります。
コンストラクタインジェクション
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Radios\Annkw;
class RadioController extends Controller
{
protected $annkw;
public function __construct(Annkw $annkw)
{
$this->annkw = $annkw;
}
public function corner(): string
{
return $this->annkw->getCorner();
}
}
クラスのコンストラクタの引数にタイプヒンティングでAnnkwクラスを注入することで、クラス内でAnnkwのインスタンスを使えるようにしています。
メソッドインジェクション
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Radios\Annkw;
class RadioController extends Controller
{
public function corner(Annkw $annkw): string
{
return $annkw->getCorner();
}
}
cornerメソッドの引数にタイプヒンティングでAnnkwクラスを注入することで、メソッド内でAnnkwのインスタンスを使えるようにしています。
上記の2つの方法で、cornerメソッドは直接Annkwクラスに依存しなくなりました。
つまり、Annkwクラスを継承したクラスなどにも差し替えることができるようになりました。(Annkwクラスを継承したクラスのイメージがつきませんが、例ということで・・・)
しかし、Annkwに少なからず依存していることは確かです。
例えば、SakumaAnnのコーナーを取得したい場合は、このクラスは利用できません。
そのような場合は、インターフェースをDIすることで対応します。
以下SakumaAnnクラスとインターフェイス、RadioController例になります。
SakumaAnn.php
<?php
namespace App\Radios;
use App\Radios\Interfaces\RadioInterface;
class SakumaAnn implements RadioInterface
{
private $name;
private $corner;
public function __construct($name = '佐久間ANN0', $corner='健康チャンス')
{
$this->name = $name;
$this->corner = $corner;
}
public function getName(): string
{
return $this->name;
}
public function getCorner(): string
{
return $this->corner;
}
}
(健康チャンスは終了しました)
RadioInterface.php
<?php
namespace App\Radios\Interfaces;
interface RadioInterface
{
public function getName(): string;
public function getCorner(): string;
}
RadioController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Radios\Interfaces\RadioInterface;
class RadioController extends Controller
{
protected $program;
public function __construct(RadioInterface $program)
{
$this->program = $program;
}
public function corner(): string
{
return $this->program->getCorner();
}
}
これで、RadioInterface.phpをimplementしたクラスであれば、どのクラスでもRadioController内で使用できるようになりました。
何より、コントローラー内に不必要な情報が少なくなりました。
しかし、このRadioInterFaceは何のインスタンスを返すのでしょうか。
正解は、サービスコンテナに保存されたインスタンスになります。
つまり、インターフェイスはサービスコンテナにバインドしなければDIとして使用することはできません。
以下、SakumaAnnを返すようにバインドした例になります。
app()->bind(RadioInterface::class, function ()
{
return New SakumaAnn();
});
これでRadioInterfaceをDIとして注入した場合、SakumaAnnのインスタンスが返されます。
以上!!!!!!!