對PHP框架中的容器的理解

2021-10-05 03:28:25 字數 4331 閱讀 1985

1:如何解決乙個類依賴另外乙個類的問題

最簡單常見的做法是,我們直接在類的內部引入該類所依賴的類,並對其進行例項化操作,下面是示例**:

class a

}class b

}

們沒用幾行**就解決了剛才提到的問題,相信很多人一開始寫**的時候都會這麼寫的。可是這種寫法存在乙個很大的問題,就是當a類發生變動的時候,比如a類的建構函式需要傳入多個引數了,這時我們需要手動的去b類中進行修改例項化a類的**,也就是說這種寫法將a類和b類耦合在了一起。 

2:如何解決上面所說的**耦合的問題,先使用手動的依賴注入方式:

大家也一定聽說過另外乙個"高大上"的名詞"依賴注入"吧,下面我們就通過手動依賴注入的方式解決上面的提出的問題,下面看**,注意是有自動依賴注入的。

//定義乙個介面

inte***ce a_parent

function test()

}//b類所依賴的a類通過實現a_parent這個介面來實現

class a implements a_parent

public function test()

}//重寫b類

class b

}

上面實現了"高大上"的依賴注入了,b類依賴於a類,我們之前的做法是在b類中引入a類進行例項化。現在是通過b類的建構函式將例項化好的a類例項通過引數的形式傳遞給b類,這樣就不需要在b類中寫引入或者例項化a類的**了,因為我們傳遞過去的就是乙個物件嘛,所以就降低了對a類的依賴。這種型別 + 引數定義形參的方式,可以提供型別約束。上面的**中,b類中約束了傳遞過來的引數必須是 a_parent型別的,正是因為有了這種引數型別的限制,才能保證我們**的可擴充套件性。大家想想,如果b類的邏輯發生變化,其現在不需要依賴a類了,而是需要與a類方法相同的另外乙個類,那我們只需要實現a_parent介面生成另外乙個類例項化作為引數即可。

3:什麼是容器,具體需要怎麼實現

上面的**我們實現了依賴注入,解決了類依賴類實現的**耦合的問題。上面的**看起來的可用了,但是在乙個比較大的專案中,類往往是非常多的,類與類之間的依賴關係也是非常的複雜的,如果我們向上面一樣,一一手動的來完成依賴的注入,也是非常麻煩的一件事情。可以想象乙個,當你要使用b類的時候,你必須需要手動的例項化a類的物件,然後在例項化b類的時候還要將這個a類的物件手動的傳參進去,是不是很麻煩。上面的a類與b類的依賴關係還不算複雜,如果還要依賴與其他的類,是不是更麻煩。了解了上面的弊端,我們就要想辦法解決它,其實容器就是專門解決上面弊端的存在...

所謂的容器就是用來裝東西的,這裡提到的容器也一樣,只不過是用來裝類的例項化物件的。如果我們需要使用乙個類的時候,直接在這個容器中取出來就好了,這樣我們如果在多處需要使用某個類的時候,就不需要在n個地方進行例項化等操作了。最關鍵的是,如果我們的類存在依賴關係,我們不需要通過上面手動的方式進行注入等操作,容器可以幫助我們分析出所依賴的類,自動完成l注入等操作。

//根據我們的上面的分析,容器至少需要倆個操作,分別是將類繫結到容器中以及將類從容器中取出的操作

class container

else

}// 生成類物件

public static function make($class_name, $params = )

return call_user_func_array(self::$generator_list[$class_name], $params);

}}

上面實現了乙個最基本的容器類,下面我們一一分析一下:

我們先看一下bind()函式,該函式對應上面說到的繫結操作,就是將乙個類放到$generator_list中,仔細看一下,你會發現,該函式並不是把乙個類或者乙個物件直接傳遞進去,而是傳入了兩個引數,乙個是引數的名字,乙個是生成器。

生成器說白了就是乙個函式,這個函式是用來負責例項化需要繫結的類的。

說到這裡,有同學可能有點疑惑,為什麼要這樣,為什麼不直接傳乙個物件進去那?

原因是類的例項化的過程是需要傳遞引數的,傳遞乙個生成器進去,我們在例項化這個類的時候就可以修改引數了。

下面就是乙個繫結示例,大家可以看一下。

container::bind("a",function($param))

self::$generator_list["a"] = function($param);

這樣,我們的繫結操作基本就說完了,下面看make()函式。之前也已經提到過了,make()函式就是將所需要的物件從這個容器中取出來。該函式也需要傳遞兩個引數進去,乙個是class_name也就是需要取出的類的名稱,乙個是params,也就是例項化物件的時候需要傳遞的引數。

下面的一行**是整個函式的關鍵所在:

call_user_func_array(self::$generator_list[$class_name], $params);

self::$generator_list[$class_name]對應的是類的生成器,$params對應的是類例項化所需要的引數,

call_user_func_array()該函式是php的內建函式,通過該函式我們可以執行self::$generator_list[$class_name]對應的是類的生成器函式,這樣我們也就是完成了所需類的例項化。(ps:對call_user_func_array()函式不清楚的同學可以先去看一下手冊)

測試**看一下:

//將類a的生成器函式(匿名函式/閉包)繫結到容器中

container::bind('a', function($name='') );

//在容器類中獲取類a的物件

$obj = container::make('a', ['aaa']);

//列印出得到的這個物件

var_dump($obj);

//列印結果如下:

object(a)#2 (1)

//我們在列印出self::$generator_list中的資料看一下:

array(1)

}}

上面我們分析了一下容器類的具體的執行方式,上面的**比較的簡單,也沒有涉及到類相互依賴的問題,大家肯定想看一下類相互依賴的時候,容器類是怎麼為我們解決依賴的,我們下面就寫乙個例子再分析一下,其實容器的**我們基本不需要在動了

//最開始的我們就舉了乙個b類依賴於a類的例子,現在我們繼續使用這個例子來說明一下

//繫結a類到容器中

container::bind('a', function($name='') );

//繫結b類到容器中

container::bind('b', function($module,$params=) );

//上面b類的繫結方式大家可能覺得有點怪,這是因為b類依賴於a類,所以我們在b類的生成器物件中(匿名函式)中需要得到a類的例項傳參給b,

//怎麼獲取a類的例項那,簡單,因為a類也存在於容器中,所以我們直接呼叫make()函式就可以獲取a類的例項物件了,

//但是在例項化a類的時候,建構函式可能需要引數,為了能夠得到這些引數,我們就需要在b類的生成器物件中將這些引數傳遞進來。

//下面我們呼叫一下b類

$obj= container::make('b', ['a', ['aaa']]);

//上面我們就獲取到了b類的例項化的物件了,是不是很簡單,有興趣的同學可以將上面的結果列印出來看一下。

//我們再分析一下上面的步驟,想要獲取b類的例項化物件,直接通過make()進行獲取,

//因為b依賴於a,所以需要傳遞a到生成器函式中,但是a有需要其他的引數,所以我們還需要繼續傳遞其他引數進去,所以引數就是乙個二維陣列

//上面對引數有疑問的同學可以按照上面的流程分析一遍,就清楚了

上面我們通過容器的方式獲取到了乙個對其他類有依賴的類的例項物件,只不過我們是通過傳參的方式完成依賴注入的。可能還覺得這樣並不高階,因為還是需要我們再繫結類的時候(也就是bind()操作的時候),需要分析某個類是否依賴其他類,如果依賴則需要在容器中獲取。當依賴變得很複雜的時候,開發維護起來還是很麻煩,這個問題怎麼解決? 

4:如何替代手工分析類的依賴問題,使用反射

想必大家都聽過這個概念,但其實在開發中使用到的概率並不高,主要是在框架開發中會用到這個功能。使用反射解決上面問題的原理就是,在例項化物件之前,先通過反射類提供的方法獲取其建構函式所需的引數,分析出其所依賴的類,然後在容器中獲取其所依賴的類,其實就是一層一層的找需要什麼,需要什麼就在容器中找什麼,找到了就作為引數傳遞過去,這樣就實現了自動注入解決了依賴的問題,是不是聽起來很簡單,但是這個反射的**寫起來需要考慮的地方還是很多的,反射也是現在框架開發中最常用的技術,也是核心技術之一。雖然反射聽起來很厲害,但是在業務開發中並不推薦使用,因為對效能的影響還是很大的。但是為了實現自動注入,又不得不使用反射。

koa框架中對next 的理解

const one ctx,next const two ctx,next const three ctx,next 輸出結果 one two three three two one 我們在定義express中介軟體函式的時候都會將第三個引數定義為next,這個next函式主要負責將控制權交給下乙個...

對Spring框架中AOP的理解

在今天讀了文章後對aop做了一些自己的理解,現記錄於此,共同交流。springaop就是所謂的切面程式設計,但要怎麼理解這個切面程式設計呢,舉乙個例子,現在我在乙個類中有a b c三個方法,這在執行這三個方法時我想要在每個方法的執行前後各輸出日誌資訊,那麼正常的操作是在每乙個方法中都要加上輸出日誌的...

理解PHP中的MVC框架程式設計

什麼是mvc mvc是乙個可以讓你把 三個部分 即mvc的全稱,model view controller 諧調地組成乙個複雜應用程式的概念。一輛汽車就是乙個在現實生活中非常好的mvc例子。我們看車都看兩個view 顯 示 部分 內部和外部。而這兩個都離不開乙個controller 控制者 司機。剎...