第1回 サービスコンテナ 「それは新しい new だった」
第2回 サービスプロバイダ 「シングルトンはたった1行」
第3回 結合 「なんでもツッコんで気軽に取り出す」
それは新しい new だった
そういえばちゃんと学んだことがなかったサービスコンテナ。
正直、ずっと、知らなくてもなんとかなるしまぁそんなのもあるよね、あーここはどうしてもそういうのがいるからとりあえず動く程度には使っておくか、という「軽いおつきあい」程度でした。
そもそも公式のドキュメントが何言ってるのか全然わかんない。
DIってなに?(書いてない)
サービスプロバイダのこと?始めるのに手順が多くてしんどい……。
と思っていましたが、改めて学んで整理してみたら、全然そんなことなかった。
超カンタンだった。
ということを、僕のように「難しそうだ」と躊躇している人に知ってもらいたい!と思って筆を執りました。
前提
- クラスとインスタンスの違いがわかる
- 公式ドキュメントの「サービスコンテナ」を読んだけどさっぱりだった。
- そもそも「サービス」って何?
- DIってなにそれおいしいの?
目標
- DIを説明せずにサービスコンテナを理解する
- 新しいクラスを書き始めるための「最小限のステップ」を定義してみる
- 公式ドキュメントに書いてある手順もできるだけ省略する!
- え?これだけでいいの!? じゃあ僕も私も今日からやってみよう、と思ってもらえる
結論
サービスコンテナはクラスのインスタンスを作ってくれるマシーン。
つまり、PHPの new を機能拡張したもの。
基本的な利用手順(とイメージされていること)
(...)はオプションです。省略可能な手順。
- サービス化したいクラスを作る
- (サービスプロバイダを作って app.php に登録する|AppServiceProviderを使う)
- (registerにインスタンス化する方法を定義しておく)
- (bootでサービス同士の依存関係を解決する)
- (ファサードを定義して app.phpに登録する)
- サービスコンテナにインスタンスを提供してもらう
あれ? 省略可? ということは…
- サービス化したいクラスを作る
- サービスコンテナにインスタンスを提供してもらう
これだけで良いんですか?
そうみたいです。ちょっと詳しく見てみましょう。
サービスコンテナ・サービスプロバイダとは?
サービスプロバイダとは?
Laravelをやっているとこちらの単語のほうがよく登場しますが、これは 「インスタンスの作り方」を「まとめて」書いておくのに便利なツール で、あってもなくてもかまいません。無くてもいいなら端折ってしまえ!
というわけで、**サービスプロバイダは要りません。**しばらく忘れてください。
#しかも実は、サービスコンテナとは何の関係ない別機能です。名前が似ているから紛らわしいだけ(サービスプロバイダで、サービスコンテナを使うことはあります。詳しくは第2回で)。
サービスコンテナとは?
一般的にはDIコンテナとかIoCコンテナと言われているものですが、公式ドキュメントでは**「サービスコンテナ」**と呼ばれています(この話は長くなりそうなので末尾のポエムにて…)。
名前はさておき、サービスコンテナって何をしてくれるの?
実は、主な機能はこの2つだけです。
- クラスのインスタンスを作る(ちょっとパワフルに)
- クラスのインスタンス等を預かってくれる
このうち「クラスのインスタンスを預かってくれる」機能は、第3回「結合」まで封印。
ここでは「インスタンスを作る」だけにフォーカスします。
サービスコンテナにクラスのインスタンスを作ってもらうには?
さて、いったんサービスコンテナを忘れましょう。
「クラスのインスタンスを作る」といえば、PHPでもできますよね?
PHPで、とあるクラス App\SampleClass のインスタンスが欲しいと思ったとき、
このように書くのはみなさんご存知の通りです。
$sampleobject = new App\SampleClass;
これと同じこと をLaravelのサービスコンテナにお願いするときは、こう書きます。
$sampleobject = app()->make('App\SampleClass');
これらはまったく同じ結果になるのに加えて、2つポイントがあります。
- このコードは、どこにでも書けます。
- このコードを書くのに特別な準備は不要です。
そう。ちょうど、PHPの new がどこにでも書けるのと同じように。
もう少し解説します。
app()はグローバル関数として定義されているので、Laravelアプリならクラスの中でも外でも、いつでもどこでも呼ぶことができます。で、呼ぶと何が出てくるかというと、サービスコンテナのインスタンスが出てきます。app() = サービスコンテナ。
また、これを使うのに、サービスプロバイダになにか書かなきゃいけないの? いいえ、何も書かなくても大丈夫。クラスがきちんと定義されているなら(PHPのnewでインスタンスが作れるなら)すぐに実行できます。先ほどサービスコンテナの機能は「インスタンスを作る」と「預かる」と書きましたが、これらは別の機能。「作る」ために「預ける(サービスプロバイダでなにか準備をする)」必要はないんです。
だからちょうど、今まで new を使っていたところはほとんどこれで置き換えができます。
これが サービスコンテナにインスタンスを提供してもらう方法。
サービスコンテナって、new なのか!?
そうそう、ちょっと乱暴に理解するとそんな感じです。
ちなみに先程の書き方は、他にもこのように書くことができます。
ドキュメントやサンプルコードなどでいろんな書き方を見るので別の機能と思いがちですが、
すべて同じ結果になる同じ機能です。
#慣れたら、app('App\SampleClass');
しか使わなくなります。
$sampleobject = app()->make('App\SampleClass'); // コンテナがインスタンスを作るよ
$sampleobject = Illuminate\Container\Container::getInstance()->make('App\SampleClass'); // コンテナを取ってきてインスタンスを作るよ
$sampleobject = app()->resolve('App\SampleClass'); // コンテナがクラスの依存を解決するよ
$sampleobject = resolve('App\SampleClass'); // クラスの依存を解決するよ
$sampleobject = app('App\SampleClass'); // インスタンスくれ
$sampleobject = Illuminate\Foundation\Application::getInstance()->make('App\SampleClass'); // アプリ本体を取ってきてインスタンスを作るよ
こんな意図かなぁというコメントも付けてみました。
最後の2行がなかなか衝撃的(笑)。もはや「Laravel=サービスコンテナ」ということ※。
Laravelやっているなら、知らないわけにはいかないってことじゃないですか!
※事実です。 class Application extends Container
です...。
まとめ
もう一度登場していただきましょう。
サービスコンテナを使う手順は、これだけです。
- サービス化したいクラスを作る
- サービスコンテナにインスタンスを提供してもらう
$sampleobject = app('App\SampleClass');
最終的にコレだけになってしまいました。
これが、サービスコンテナの基本的な機能とその使い方。
驚くほどカンタン、というのは、誰を隠そう、僕自身の感想です♬
これを読んだ人も試しにやってみて、その手軽さに驚いていただけると嬉しいです。
ちょっとまてカンタンすぎるぞ!
そうです、すみません。確かにちょっと端折りすぎました。
まずは基本機能がどれだけカンタンかを知ってほしかったんです!
だから僕も心を鬼にして、初稿の半分以上をボツにしてテーマを絞りました。
実は上にもちらっと書きましたが、この超基本機能の「クラスのインスタンスを作る」は、
newよりも ちょっとパワフル です。
それもあって、一部のクラスは app(Class) でインスタンスを作ってもらうことができません。
次回はそのパワーの源、「コンストラクタインジェクション」に触れてみたいと思います。
といいつつ、先に「サービスプロバイダ」と最低限知っておいたほうが良い「結合」について、シングルトンを題材に書いていますが…。近日、再編集予定です。
第1回 サービスコンテナ 「それは新しい new だった」
第2回 サービスプロバイダ 「シングルトンはたった1行」
第3回 結合 「なんでもツッコんで気軽に取り出す」
その勢いで、スタティック地獄を抜け出すためのファサード講座とか、マニアックに技術を追求するメソッドインジェクション解体新書とか、LaravelをLaravelたらしめるクラスだけじゃなくてなんでも載っかるサービスコンテナの真実、とか書いてみたいですね。(いつかそのうち…)(チカラが及べば……)
ポエム:DIってなんだ?
広告をちょっとかじってきた経験があるので「ネーミングはホントに大事だな」とよく意識が向いてしまいます。名前がおかしいと、それを聞いた人が理解しにくいだけでなく誤解を招くことがある。逆にそうならない名前をつけてあげないといけない。
その典型例が「DI・依存注入」という名前。
DIってなんだ? そもそも名前の意味がわからない。dependencyもinjectionも依存も注入も聞き慣れなくてイメージできない。なんでこんな名前かっていうとね……と説明を始めなきゃいけなくて、その説明だけでなんだかわからない難しいもののような気がする。
でも実はDIは難しくない。悪いのは、名前。
もう1つ話をややこしくしているとしたら、DIっていうのは単なるデザインパターン(設計の考え方)であって、それを具体的に解決してくれる何か、いわば「DI解決マシーン」が必要なんですよね。その「DI解決マシーン」の1つが、PHPのLaravelに実装されている「サービスコンテナ」。
出来上がったそれはDIをとてもエレガントに解決してくれたから、DIは何かを考える機会が減っていって、だんだん「DI」という名前は必要なくなっていくんじゃないかな。
ものすごく極端に言うと、 「洗濯機」という単語がまだ世の中になくて、代わりに「洗剤注入」と呼んでいるような状況 なのかなー。きっと他にももっとすごい「洗濯機」があるかもしれないし、この先登場するかもしれない。そうなったらきっと「洗剤注入」という言葉は使われなくなっている、かもしれない。