Laravelのサービスコンテナとは
サービスコンテナとは
DI(依存性の注入)を手助けしてくれるツール
主なサービスコンテナの機能
-
依存解決
コンストラクタで型宣言したクラスを自動でインスタンス化して渡してくれる。
まじで魔法。どうなってんの。
-
結合
インスタンス化方法をカスタマイズできる
app()->bind('呼び出しキーワード', 'インスタンス化方法');
結合とは
以下のようにbind
メソッドによるインスタンス化方法の記述のこと。
返す値はinterface
か実際に生成されるインスタンス(インスタンスの場合singletonでの記述をよく見る気がするので、インスタンスを返すのならそちらのほうがいいのかもしれない)
app()->bind(ClassA::class, function ($app) {
$classB = $app()->make(ClassB::class);
return $classB;
});
以下のようにFacadeを使ってproxy化した形で使用することもできる。
App::bind(Request::class, function ($app) {
// Requestクラスの呼び出しをカスタマイズ
});
ファサード(Facade)とは
サービスコンテナ内の基礎となるクラスへの「静的プロキシ」として機能する。
静的プロキシとは?
プロキシパターン(Proxy Pattern)とも呼ばれる。(逆に動的プロキシもある)
(Laravelの説明なのにGoが好きなのでなぜかGoで書いてます。すいません)
type MyInterface interface {
myMethod(string str) err;
}
type MyClass struct {
}
func (m MyClass) myMethod(string str) err {
Println("myMethod >>>", str);
return nil;
}
一般的なプロキシパターンを利用せずにMyClass構造体を呼び出した時(以下)
以下ではMyClass
オブジェクトの利用者は実際に紐付いているmyMethod
関数を使用している。
myclass := &MyClass{}
Println(myclass.myMethod("not proxy patern"));
// "myMethod >>> not proxy patern"
Proxy Paternで呼び出すには
同じinterfaceの実装を内部的にもたせればいい。
GoではnewがないのでNewMyproxyを実装するのと、その中で構造体にtargetObj
をもたせてそこに実際のオブジェクトをセットするようにする。
内部のtargetObj
はproxyの対象となるオブジェクトのinterface型にする。
Proxyオブジェクト経由で同じ返り値が返されるように返り値に気をつけること。
メリットとしては、本体の実装を一切変えないで機能を拡張できるというところ。
type MyProxy struct {
proxyObj MyInterface;
}
func NewMyProxy (MyInterface wrapedObj) MyProxy {
return &MyProxy{
proxyObj: wrapedObj,
}
}
func (m MyProxy) myMethod(string str) err {
Println("myProxyMethod...");
result := m.proxyObj.myMethod(str);
return result;
}
Proxyの使用を強制するような実装にすることもできる。
Facadeの注意点
クラスの 「スコープクリープ」
クラスのスコープが簡単に肥大化してしまう恐れがある。ひとつのクラスを拡大することが用意であるがゆえの注意点である。
インターフェイスと実装の結合
サービスコンテナの魔法機能であるタイプヒントによるインスタンスの呼び出しもとを決定できる。
以下の例はCarInterface
を呼び出したときに、CarClass
のインスタンスを返すというもの。
つまり、以下のようなbind
を行うことでCarInterface
をコンストラクタの引数に取るクラスのインスタンスの自動生成ができるようになるということ。そして、その際に自動生成されるインスタンスはCarClass
のインスタンスである。
app()->bind(CarInterface::class, CarClass::class);
class Toyota () {
private $car;
public function constructor(CarInterface $car){
this->car = $car;
}
}
Toyota Classを解決する際に効果を発揮する。
// constructorを指定せずともCarClassのインスタンスが$toyota->$carに格納される
$toyota = app()->make(Toyota::class);
参照記事