什麼是依賴
打個比方,就好比每個人都需要穿衣服才能進行正常的社會活動,這就是說是個人都需要依賴衣服才能正常社會活動。
相應的,就是一個類需要另一個類才能完成工作,這就是依賴
code demo
class Person{
protected $clothes;
public function __construct()
{
$this->clothes = new Clothes();
}
}
看上面的程式碼就知道 人依賴了衣服,
什麼是依賴注入呢,我們改造下上面的程式碼
class Person {
protected $clothes;
public function __construct(Clothes $clothes) {
$this->clothes = $clothes;
}
}
$person = new Person(new Clothes());
這裡的Person 依賴注入了clothes類
理解了依賴注入,我們就可以接著去理解IOC
IOC
IOC 是什麼呢,看明白了依賴注入(DI)後就很容易理解了
通過DI我們可以看到,一個類所需要的依賴類是由我們主動例項化後傳入類中的。
控制反轉的意思就是說將依賴類的控制權交出去,由主動變被動。
laravel 程式碼
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class AuthController extends Controller
{
public function login(Request $request)
{
//這就是IOC,我們不需要主動傳入類了一切由laravel去實現
}
}
看到這你可能有疑問了,這是怎麼實現的呢?
這就是靠服務容器了,請往下接著看。
服務容器
看了很多文章,我一致認為服務容器就是一種設計模式。
它的目的就是解耦依賴。
它有點類似於我前面說的《享元模式》。區別在於服務容器解決了所有依賴的實現。
這裡我們再從頭至尾的看一遍,怎麼一步步演化出服務容器。
依然是人的例子,我們知道人依賴衣服鞋子褲子,可是衣服鞋子也有很多種品牌呀。
先看一個最原始的程式碼例子:
class Person {
protected $clothes;
public function __construct($type = null) {
switch($type) {
case 'anta':
$this->clothes = new Anta();
case 'adidas':
$this->clothes = new Adidas();
default:
$this->clothes = new 360();
}
}
}
或許你一眼就看出了問題在哪。
如果我們又要增加一種衣服,那我們又得對這個類進行修改。這樣下去,這個類會變得龐大且耦合程度過高。
那麼我們可以怎麼修改呢?
工廠模式
這樣我們可以避免直接的修改 Person 類。
class Factory {
public static function getInstance($type){
switch($type) {
case 'anta':
$this->clothes = new Anta();
break;
case 'adidas':
$this->clothes = new Adidas();
break;
default:
$this->clothes = new 360();
break;
}
}
}
class Person {
protected $clothes;
public function __construct($type == null) {
$this->clothes = Factory::getInstance($type);
}
}
這樣使用簡單工廠模式後,我們後續的修改可以不用對 Person 類進行操作而只要修改工廠類就行了。這就相當於對 Person 類進行了解耦。
Person 類雖不在依賴那些鍵盤類了,但是卻變為依賴工廠類了。
後續新增新型別的鍵盤就必須對工廠類進行修改。
所以這個工廠類還不能很好的滿足要求,我們知道電腦對鍵盤的介面都是一致的,鍵盤必須實現這一介面才能被電腦識別,那我們對 Person 和 Clothes 類進行修改。
DI(依賴注入)
interface Clothes {
public function type();
}
class AntaClothes implements Clothes {
public function type(){
echo '安踏';
}
}
class AdidasClothes implements Clothes {
public function type(){
echo 'adidas';
}
}
class Person {
protected $clothes;
public function __construct (Clothes $clothes) {
$this->clothes = $clothes;
}
}
$person = new Person(new AntaClothes());
可是這樣也有問題,如果我們後續有人使用的衣服不滿意要進行替換呢? 我們又回到原點了,必須去修改傳入的衣服類。
能不能做成可配置的呢?
IOC服務容器(超級工廠)
class Container
{
protected $binds;
protected $instances;
public function bind($abstract, $concrete)
{
if ($concrete instanceof Closure) {
$this->binds[$abstract] = $concrete;
} else {
$this->instances[$abstract] = $concrete;
}
}
public function make($abstract, $parameters = [])
{
if (isset($this->instances[$abstract])) {
return $this->instances[$abstract];
}
array_unshift($parameters, $this);
return call_user_func_array($this->binds[$abstract], $parameters);
}
}
這就是一個簡單的 IOC 服務容器。
這個怎麼解決我們上述的問題呢?
$container = new Container;
$container->bind('Clothes', function($container){
return new AntaClothes;
});
$container->bind('Person',function($container,$module){
return new Person($container->make($module));
});
$person = $container->make('Person',['Clothes']);
這裡生產出來的 Person 類就是一個使用了安踏衣服的類了。
如果我們要換衣服怎麼辦呢?
$container->bind('Clothes', function($container){
return new AdidasClothes;
});
$container->bind('Person',function($container,$module){
return new Person($container->make($module));
});
$person = $container->make('Person', ['Clothes']);
只要對 bind 繫結的 Clothes 類的實現進行修改,我們就可以很容換掉衣服了。這就是一個服務容器。
對服務容器進行一個理解:
容器就是一個裝東西的,好比碗。而服務就是這個碗要裝的飯呀,菜呀,等等東西。當我們需要飯時,我們就能從這個碗裡拿到。如果你想在飯里加點菜(也就是飯依賴注入了菜),我們從碗裡直接拿飯就可以了,而這些依賴都由容器解決了(這也就是控制反轉)。
我們需要做的就是對提供的服務進行維護。
本作品採用《CC 協議》,轉載必須註明作者和本文連結