理解 PHP 依賴注入 和 控制反轉


要想理解 PHP 依賴注入 和 控制反轉 兩個概念,就必須搞清楚如下的兩個問題:

  • DI —— Dependency Injection 依賴注入
  • IoC —— Inversion of Control 控制反轉

什么是依賴注入

沒有你我就活不下去,那么,你就是我的依賴。 說白了就是:

不是我自身的,卻是我需要的,都是我所依賴的。一切需要外部提供的,都是需要進行依賴注入的。

依賴注入舉例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class  Boy {
   protected  $girl ;
 
   public  function  __construct(Girl  $girl ) {
     $this ->girl =  $girl ;
   }
}
 
class  Girl {
   ...
}
 
$boy  new  Boy();   // Error; Boy must have girlfriend!
 
// 所以,必須要給他一個女朋友才行
$girl  new  Girl();
 
$boy  new  Boy( $girl );  // Right! So Happy!

從上述代碼我們可以看到Boy強依賴Girl必須在構造時注入Girl的實例才行。

那么為什么要有依賴注入這個概念,依賴注入到底解決了什么問題?

我們將上述代碼修正一下我們初學時都寫過的代碼:

1
2
3
4
5
6
7
class  Boy {
   protected  $girl ;
 
   public  function  __construct() {
     $this ->girl =  new  Girl();
   }
}

這種方式與前面的方式有什么不同呢?

我們會發現Boy的女朋友被我們硬編碼到Boy的身體里去了。。。 每次Boy重生自己想換個類型的女朋友都要把自己扒光才行。

某天Boy特別喜歡一個LoliGirl ,非常想讓她做自己的女朋友。。。怎么辦? 重生自己。。。扒開自己。。。把Girl扔了。。。把 LoliGirl塞進去。。。

1
2
3
4
5
6
7
8
9
10
11
12
class  LoliGirl {
 
}
 
class  Boy {
   protected  $girl ;
 
   public  function  __construct() {
       //  $this->girl = new Girl();  // sorry...
       $this ->girl =  new  LoliGirl();
   }
}

某天 Boy迷戀上了御姐....Boy 好煩。。。

是不是感覺不太好?每次遇到真心相待的人卻要這么的折磨自己。。。

Boy說,我要變的強大一點。我不想被改來改去的!

好吧,我們讓Boy強大一點:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
interface  Girl {
   // Boy need knows that I have some abilities.
}
 
class  LoliGril implement Girl {
   // I will implement Girl's abilities.
}
 
class  Vixen implement Girl {
   // Vixen definitely is a girl, do not doubt it.
}
 
class  Boy {
   protected  $girl ;
 
   public  function  __construct(Girl  $girl ) {
     $this ->girl =  $girl ;
   }
}
 
$loliGirl  new  LoliGirl();
$vixen  new  Vixen();
 
$boy  new  Boy( $loliGirl );
$boy  new  Boy( $vixen );

Boy 很高興,終於可以不用扒開自己就可以體驗不同的人生了。。。So Happy!

依賴注入方式

1、構造器 注入

1
2
3
4
5
6
7
8
<?php
class  Book {
   private  $db_conn ;
  
   public  function  __construct( $db_conn ) {
     $this ->db_conn =  $db_conn ;
   }
}

2、setter 注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
class  Book {
     private  $db ;
     private  $file ;
 
     function  setdb( $db ) {
         $this ->db =  $db ;
     }
 
     function  setfile( $file ) {
         $this ->file =  $file ;
     }
}
 
class  file {
}
 
class  db {
}
 
// ...
 
class  test {
     $book  new  Book();
     $book ->setdb( new  db());
     $book ->setfile( new  file());
}

小結:

因為大多數應用程序都是由兩個或者更多的類通過彼此合作來實現業務邏輯,這使得每個對象都需要獲取與其合作的對象(也就是它所依賴的對象)的引用。如果這個獲取過程要靠自身實現,那么將導致代碼高度耦合並且難以維護和調試。

所以才有了依賴注入的概念,依賴注入解決了以下問題:

  • 依賴之間的解耦
  • 單元測試,方便Mock

上面倆種方法代碼很清晰,但是當我們需要注入很多個依賴時,意味着又要增加很多行,會比較難以管理。

比較好的解決辦法是 建立一個class作為所有依賴關系的container,在這個class中可以存放、創建、獲取、查找需要的依賴關系。先來了解一下IOC的概念

控制反轉 (Inversion Of Control, IOC)

控制反轉 是面向對象編程中的一種設計原則,可以用來減低計算機代碼之間的耦合度。其中最常見的方式叫做 依賴注入(Dependency Injection, DI), 還有一種叫"依賴查找"(Dependency Lookup)。通過控制反轉,對象在被創建的時候,由一個調控系統內所有對象的外界實體,將其所依賴的對象的引用傳遞給它。也可以說,依賴被注入到對象中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
 
class  Ioc {
     protected  $db_conn ;
 
     public  static  function  make_book() {
         $new_book  new  Book();
         $new_book ->set_db(self:: $db_conn );
         //...
         //...
         //其他的依賴注入
         return  $new_book ;
     }
}

此時,如果獲取一個book實例,只需要執行$newone = Ioc::makebook();

以上是container的一個具體實例,最好還是不要把具體的某個依賴注入寫成方法,采用registry注冊,get獲取比較好

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?php
/**
  * 控制反轉類
  */
class  Ioc {
     /**
      * @var array 注冊的依賴數組
      */
     protected  static  $registry  array ();
 
     /**
      * 添加一個 resolve (匿名函數)到 registry 數組中
      *
      * @param string  $name    依賴標識
      * @param Closure $resolve 一個匿名函數,用來創建實例
      * @return void
      */
     public  static  function  register( $name , Closure  $resolve ) {
         static :: $registry [ $name ] =  $resolve ;
     }
 
     /**
      * 返回一個實例
      *
      * @param string $name 依賴的標識
      * @return mixed
      * @throws \Exception
      */
     public  static  function  resolve( $name ) {
         if  ( static ::registered( $name )) {
             $name  static :: $registry [ $name ];
             return  $name ();
         }
 
         throw  new  \Exception( "Nothing registered with that name" );
     }
 
     /**
      * 查詢某個依賴實例是否存在
      *
      * @param string $name
      * @return bool
      */
     public  static  function  registered( $name ) {
         return  array_key_exists ( $name static :: $registry );
     }
}

現在就可以通過如下方式來注冊和注入一個

1
2
3
4
5
6
7
8
9
10
11
<?php
Ioc::register( "book" function  () {
     $book  new  Book();
     $book ->setdb( 'db' );
     $book ->setfile( 'file' );
 
     return  $book ;
});
 
// 注入依賴
$book  = Ioc::resolve( 'book' );

問題匯總

1、參與者都有誰?

答:一般有三方參與者,一個是某個對象;一個是IoC/DI的容器;另一個是某個對象的外部資源。又要名詞解釋一下,某個對象指的就是任意的、普通的Java對象; IoC/DI的容器簡單點說就是指用來實現IoC/DI功能的一個框架程序;對象的外部資源指的就是對象需要的,但是是從對象外部獲取的,都統稱資源,比如:對象需要的其它對象、或者是對象需要的文件資源等等。

2、依賴:誰依賴於誰?為什么會有依賴?

答:某個對象依賴於IoC/DI的容器。依賴是不可避免的,在一個項目中,各個類之間有各種各樣的關系,不可能全部完全獨立,這就形成了依賴。傳統的開發是使用其他類時直接調用,這會形成強耦合,這是要避免的。依賴注入借用容器轉移了被依賴對象實現解耦。

3、注入:誰注入於誰?到底注入什么?

答:通過容器向對象注入其所需要的外部資源

4、控制反轉:誰控制誰?控制什么?為什么叫反轉?

答:IoC/DI的容器控制對象,主要是控制對象實例的創建。反轉是相對於正向而言的,那么什么算是正向的呢?考慮一下常規情況下的應用程序,如果要在A里面使用C,你會怎么做呢?當然是直接去創建C的對象,也就是說,是在A類中主動去獲取所需要的外部資源C,這種情況被稱為正向的。那么什么是反向呢?就是A類不再主動去獲取C,而是被動等待,等待IoC/DI的容器獲取一個C的實例,然后反向的注入到A類中。

5、依賴注入和控制反轉是同一概念嗎?

答:從上面可以看出:依賴注入是從應用程序的角度在描述,可以把依賴注入描述完整點:應用程序依賴容器創建並注入它所需要的外部資源;而控制反轉是從容器的角度在描述,描述完整點:容器控制應用程序,由容器反向的向應用程序注入應用程序所需要的外部資源。

轉載:https://www.cnblogs.com/52php/p/6379020.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM