#開発者として、私たちはデザイン パターンを使用したり、新しい堅牢なフレームワークを試したりすることで、適切に設計された堅牢なコードを作成する新しい方法を常に見つけようとしています。この記事では、Laravel の IoC コンポーネントを使用した依存性注入の設計パターンを調査し、それがどのように設計を改善できるかを見ていきます。
依存性注入という用語は、Martin Fowler によって提案された用語であり、アプリケーションにコンポーネントを注入する行為です。 Ward Cunningham 氏は次のように述べています:
依存性注入はアジャイル アーキテクチャの重要な要素です。
例を見てみましょう:
class UserProvider{ protected $connection; public function __construct(){ $this->connection = new Connection; } public function retrieveByCredentials( array $credentials ){ $user = $this->connection ->where( 'email', $credentials['email']) ->where( 'password', $credentials['password']) ->first(); return $user; } }
このクラスをテストまたは保守したい場合は、データベース インスタンスにアクセスしていくつかのクエリを実行する必要があります。これを回避するには、このクラスを他のクラスから 分離 することができます。Connection
クラスを直接使用せずに挿入する 3 つのオプションのいずれかがあります。
コンポーネントをクラスに挿入するときは、次の 3 つのオプションのいずれかを使用できます。
class UserProvider{ protected $connection; public function __construct( Connection $con ){ $this->connection = $con; } ...
#同様に、Setter メソッドを使用して依存関係を注入することもできます。
class UserProvider{ protected $connection; public function __construct(){ ... } public function setConnection( Connection $con ){ $this->connection = $con; } ...
interface ConnectionInjector{ public function injectConnection( Connection $con ); } class UserProvider implements ConnectionInjector{ protected $connection; public function __construct(){ ... } public function injectConnection( Connection $con ){ $this->connection = $con; } }
クラスがインターフェイスを実装するとき、次のように定義しますinjectConnection
メソッドを使用して依存関係を解決します。
クラスをテストするときに、依存クラスをモックしてパラメーターとして渡すことができるようになりました。各クラスは特定のタスクに重点を置く必要があり、依存関係の解決には関心を持たないでください。こうすることで、より集中的で保守しやすいアプリケーションが作成されます。
DI についてさらに詳しく知りたい場合は、Alejandro Gervassio がこの連載記事で広範囲かつ専門的に取り上げていますので、必ずお読みください。では、IoCとは何でしょうか? IoC (制御の反転) では依存関係の注入を使用する必要はありませんが、依存関係を効果的に管理するのに役立ちます。
Ioc は、依存関係の解決を容易にする単純なコンポーネントです。オブジェクトをコンテナとして記述することができ、クラスが解決されるたびに依存関係が自動的に挿入されます。
Laravel Ioc は、オブジェクトをリクエストするときに依存関係を解決する方法が少し特殊です:
私たちは A を使用します。この記事では、簡単な例でそれを改善します。 SimpleAuth
クラスは FileSessionStorage
に依存するため、コードは次のようになります:
class FileSessionStorage{ public function __construct(){ session_start(); } public function get( $key ){ return $_SESSION[$key]; } public function set( $key, $value ){ $_SESSION[$key] = $value; } } class SimpleAuth{ protected $session; public function __construct(){ $this->session = new FileSessionStorage; } } //创建一个 SimpleAuth $auth = new SimpleAuth();
これは古典的なアプローチです。コンストラクター Function の使用から始めましょう。注射が始まります。
class SimpleAuth{ protected $session; public function __construct( FileSessionStorage $session ){ $this->session = $session; } }
次にオブジェクトを作成します:
$auth = new SimpleAuth( new FileSessionStorage() );
Laravel Ioc を使用してこれらすべてを管理したいと思います。
Application
クラスは Container
クラスを継承しているため、App
ファサードを通じてコンテナにアクセスできます。
App::bind( 'FileSessionStorage', function(){ return new FileSessionStorage; });
bind
メソッドの最初のパラメータはコンテナにバインドされる一意の ID で、2 番目のパラメータは FileSessionStorage## が実行されるたびに実行されるコールバック関数です。 # クラスが実行されますが、以下に示すようにクラス名を表す文字列を渡すこともできます。
注: Laravel パッケージを見ると、バインディングが ( view、
view.finder## など) のようにグループ化されていることがわかります。 # ...)。 セッション ストアを MySQL ストレージに変換すると仮定すると、クラスは次のようになります。
class MysqlSessionStorage{ public function __construct(){ //... } public function get($key){ // do something } public function set( $key, $value ){ // do something } }
依存関係を変更したので、
SimpleAuth も変更する必要があります。関数を構築し、新しいオブジェクトをコンテナにバインドします。
抽象化は詳細に依存すべきではなく、詳細は抽象化に依存する必要があります。SimpleAuth私たちの
Robert C. Martin
クラスは、ストレージがどのように行われるかを気にする必要はなく、代わりにサービスの利用に重点を置く必要があります。 したがって、ストレージを抽象的に実装できます:
interface SessionStorage{ public function get( $key ); public function set( $key, $value ); }
これにより、
SessionStorage インターフェイスのインスタンスを実装して要求できます: <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">class FileSessionStorage implements SessionStorage{
public function __construct(){
//...
}
public function get( $key ){
//...
}
public function set( $key, $value ){
//...
}
}
class MysqlSessionStorage implements SessionStorage{
public function __construct(){
//...
}
public function get( $key ){
//...
}
public function set( $key, $value ){
//...
}
}
class SimpleAuth{
protected $session;
public function __construct( SessionStorage $session ){
$this->session = $session;
}
}</pre><div class="contentsignin">ログイン後にコピー</div></div>
If
を使用して、コンテナを通じて SimpleAuth
クラスを解決します。コンテナは、問題を解決しようとして
BindingResolutionException
をスローします。バインディングからのクラス その後、リフレクション メソッドに戻り、すべての依存関係を解決します。 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">Uncaught exception 'Illuminate\Container\BindingResolutionException' with message 'Target [SessionStorage] is not instantiable.'</pre><div class="contentsignin">ログイン後にコピー</div></div>
コンテナはインターフェイスをインスタンス化しようとしています。このインターフェースに特定のバインディングを作成できます。
App:bind( 'SessionStorage', 'MysqlSessionStorage' );
现在每次我们尝试从容器解析该接口时,我们会得到一个 MysqlSessionStorage
实例。如果我们想要切换我们的存储服务,我们只要变更一下这个绑定。
Note: 如果你想要查看一个类是否已经在容器中被绑定,你可以使用 App::bound('ClassName')
,或者可以使用 App::bindIf('ClassName')
来注册一个还未被注册过的绑定。
Laravel Ioc 也提供 App::singleton('ClassName', 'resolver')
来处理单例的绑定。
你也可以使用 App::instance('ClassName', 'instance')
来创建单例的绑定。
如果容器不能解析依赖项就会抛出 ReflectionException
,但是我们可以使用 App::resolvingAny(Closure)
方法以回调函数的形式来解析任何指定的类型。
Note: 如果你为某个类型已经注册了一个解析方式 resolvingAny
方法仍然会被调用,但它会直接返回 bind
方法的返回值。
global/start.php
中,但如果项目变得越来越庞大就有必要使用 Service Provider 。php artisan tinker
,它十分强大,且能帮你提升你的 Laravel 测试流程。和往常一样,学习或者了解某些东西最好的方法就是查看源代码。Laravel Ioc 仅仅只是一个文件,不会花费你太多时间来完成所有功能。你想了解更多关于 Laravel Ioc 或者 Ioc 的一般情况吗?那请告诉我们吧!
推荐教程:《Laravel教程》
以上がLaravel の依存性注入と IoCの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。