依賴注入是敏捷架構中關鍵元素。讓我們來看一個例子:
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 類別注入而不需要直接使用它。
class UserProvider{ protected $connection; public function __construct( Connection $con ){ $this->connection = $con; } ...
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 方法來解決依賴關係。
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();這是一個經典的方法,讓我們從使用建構函數注入開始。
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 ,第二個參數是一個回呼函數每當執行
FileSessionStorage 類別時執行,我們也可以傳遞一個表示類別名稱的字串,如下所示。
Note: 如果你查看Laravel 套件時,你會看到綁定有時會分組,例如( view,
view.finder# ……)。
class MysqlSessionStorage{ public function __construct(){ //... } public function get($key){ // do something } public function set( $key, $value ){ // do something } }現在我們已經更改了依賴項,我們還需要更改
SimpleAuth 構造函數,並將新物件綁定到容器中!
高階模組不應該依賴低階模組,兩者都應該依賴抽象物件。我們的抽像不應該依賴細節,細節應該取決於抽象。
Robert C. Martin
SimpleAuth 類別不應該關心我們的儲存是如何完成的,相反地它更應該關注於消費的服務。
interface SessionStorage{ public function get( $key ); public function set( $key, $value ); }這樣我們就可以實作並請求
SessionStorage 介面的實例:
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; } }如果我們使用
App::make('SimpleAuth') 透過容器解析
SimpleAuth類,容器將會拋出
BindingResolutionException ,試著從綁定解析類之後,返回反射方法並解析所有依賴項。
Uncaught exception 'Illuminate\Container\BindingResolutionException' with message 'Target [SessionStorage] is not instantiable.'容器正試圖將介面實例化。我們可以為該介面做一個具體的綁定。
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中文網其他相關文章!