




版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、依賴注入與服務(wù)定位器(Dependency Injection/Service Location)¶接下來(lái)的例子有些長(zhǎng),但解釋了為什么我們使用依賴注入與服務(wù)定位器. 首先,假設(shè)我們正在開(kāi)發(fā)一個(gè)組件,叫SomeComponent,它執(zhí)行的內(nèi)容現(xiàn)在還不重要。 我們的組件需要依賴數(shù)據(jù)庫(kù)的連接。在下面第一個(gè)例子中,數(shù)據(jù)庫(kù)的連接是在組件內(nèi)部建立的。這種方法是不實(shí)用的;事實(shí)上這樣做的話,我們不能改變創(chuàng)建數(shù)據(jù)庫(kù)連接的參數(shù)或者選擇不同的數(shù)據(jù)庫(kù)系統(tǒng),因?yàn)檫B接是當(dāng)組件被創(chuàng)建時(shí)建立的。<?phpclass SomeComponent /* * 連接數(shù)據(jù)庫(kù)的實(shí)例是被寫(xiě)死在組件的內(nèi)部 * 因此,我們很難
2、從外部替換或者改變它的行為 */ public function someDbTask() $connection = new Connection(array( "host" => "localhost", "username" => "root", "password" => "secret", "dbname" => "invo" ); / . $some = new SomeComponent();$so
3、me->someDbTask();為了解決這樣的情況,我們建立一個(gè)setter,在使用前注入獨(dú)立外部依賴?,F(xiàn)在,看起來(lái)似乎是一個(gè)不錯(cuò)的解決辦法:<?phpclass SomeComponent protected $_connection; /* * 設(shè)置外部傳入的數(shù)據(jù)庫(kù)的連接實(shí)例 */ public function setConnection($connection) $this->_connection = $connection; public function someDbTask() $connection = $this->_connection; / .
4、 $some = new SomeComponent();/建立數(shù)據(jù)庫(kù)連接實(shí)例$connection = new Connection(array( "host" => "localhost", "username" => "root", "password" => "secret", "dbname" => "invo");/向組件注入數(shù)據(jù)連接實(shí)例$some->setConnection($connect
5、ion);$some->someDbTask();想一下,假設(shè)我們使用這個(gè)組件在應(yīng)用內(nèi)的好幾個(gè)地方都用到,然而我們?cè)谧⑷脒B接實(shí)例時(shí)還需要建立好幾次數(shù)據(jù)的連接實(shí)例。 如果我們可以獲取到數(shù)據(jù)庫(kù)的連接實(shí)例而不用每次都要?jiǎng)?chuàng)建新的連接實(shí)例,使用某種全局注冊(cè)表可以解決這樣的問(wèn)題:<?phpclass Registry /* * 返回?cái)?shù)據(jù)庫(kù)連接實(shí)例 */ public static function getConnection() return new Connection(array( "host" => "localhost", "us
6、ername" => "root", "password" => "secret", "dbname" => "invo" ); class SomeComponent protected $_connection; /* * 設(shè)置外部傳入的數(shù)據(jù)庫(kù)的連接實(shí)例 */ public function setConnection($connection) $this->_connection = $connection; public function someDbT
7、ask() $connection = $this->_connection; / . $some = new SomeComponent();/把注冊(cè)表中的連接實(shí)例傳遞給組件$some->setConnection(Registry:getConnection();$some->someDbTask();現(xiàn)在,讓我們?cè)O(shè)想一下,我們必須實(shí)現(xiàn)2個(gè)方法,第一個(gè)方法是總是創(chuàng)建一個(gè)新的連接,第二方法是總是使用一個(gè)共享連接:<?phpclass Registry protected static $_connection; /* * 建立一個(gè)新的連接實(shí)例 */ protected
8、 static function _createConnection() return new Connection(array( "host" => "localhost", "username" => "root", "password" => "secret", "dbname" => "invo" ); /* * 只建立一個(gè)連接實(shí)例,后面的請(qǐng)求只返回該連接實(shí)例 */ public static funct
9、ion getSharedConnection() if (self:$_connection=null) $connection = self:_createConnection(); self:$_connection = $connection; return self:$_connection; /* * 總是返回一個(gè)新的連接實(shí)例 */ public static function getNewConnection() return self:_createConnection(); class SomeComponent protected $_connection; /* * 設(shè)置
10、外部傳入的數(shù)據(jù)庫(kù)的連接實(shí)例 */ public function setConnection($connection) $this->_connection = $connection; /* * 這個(gè)方法總是需要共享連接實(shí)例 */ public function someDbTask() $connection = $this->_connection; / . /* * 這個(gè)方法總是需要新的連接實(shí)例 */ public function someOtherDbTask($connection) $some = new SomeComponent();/注入共享連接實(shí)例$some
11、->setConnection(Registry:getSharedConnection();$some->someDbTask();/這里我們總是傳遞一個(gè)新的連接實(shí)例$some->someOtherDbTask(Registry:getConnection();到目前為止,我們已經(jīng)看到依賴注入怎么解決我們的問(wèn)題了。把依賴作為參數(shù)來(lái)傳遞,而不是建立在內(nèi)部建立它們,這使我們的應(yīng)用更加容易維護(hù)和更加解耦。不管怎么樣,長(zhǎng)期來(lái)說(shuō),這種形式的依賴注入有一些缺點(diǎn)。例如,如果這個(gè)組件有很多依賴, 我們需要?jiǎng)?chuàng)建多個(gè)參數(shù)的setter方法來(lái)傳遞依賴關(guān)系,或者建立一個(gè)多個(gè)參數(shù)的構(gòu)造函數(shù)來(lái)傳遞它們
12、,另外在使用組件前還要每次都創(chuàng)建依賴,這讓我們的代碼像這樣不易維護(hù):<?php/創(chuàng)建依賴實(shí)例或從注冊(cè)表中查找$connection = new Connection();$session = new Session();$fileSystem = new FileSystem();$filter = new Filter();$selector = new Selector();/把實(shí)例作為參數(shù)傳遞給構(gòu)造函數(shù)$some = new SomeComponent($connection, $session, $fileSystem, $filter, $selector);/ . 或者使用s
13、etter$some->setConnection($connection);$some->setSession($session);$some->setFileSystem($fileSystem);$some->setFilter($filter);$some->setSelector($selector);假設(shè)我們必須在應(yīng)用的不同地方使用和創(chuàng)建這些對(duì)象。如果當(dāng)你永遠(yuǎn)不需要任何依賴實(shí)例時(shí),你需要去刪掉構(gòu)造函數(shù)的參數(shù),或者去刪掉注入的setter。為了解決這樣的問(wèn)題,我們?cè)俅位氐饺肿?cè)表創(chuàng)建組件。不管怎么樣,在創(chuàng)建對(duì)象之前,它增加了一個(gè)新的抽象層:<?p
14、hpclass SomeComponent / . /* * Define a factory method to create SomeComponent instances injecting its dependencies */ public static function factory() $connection = new Connection(); $session = new Session(); $fileSystem = new FileSystem(); $filter = new Filter(); $selector = new Selector(); return
15、 new self($connection, $session, $fileSystem, $filter, $selector); 瞬間,我們又回到剛剛開(kāi)始的問(wèn)題了,我們?cè)俅蝿?chuàng)建依賴實(shí)例在組件內(nèi)部!我們可以繼續(xù)前進(jìn),找出一個(gè)每次能奏效的方法去解決這個(gè)問(wèn)題。但似乎一次又一次,我們又回到了不實(shí)用的例子中。一個(gè)實(shí)用和優(yōu)雅的解決方法,是為依賴實(shí)例提供一個(gè)容器。這個(gè)容器擔(dān)任全局的注冊(cè)表,就像我們剛才看到的那樣。使用依賴實(shí)例的容器作為一個(gè)橋梁來(lái)獲取依賴實(shí)例,使我們能夠降低我們的組件的復(fù)雜性:<?phpclass SomeComponent protected $_di; public functi
16、on _construct($di) $this->_di = $di; public function someDbTask() / 獲得數(shù)據(jù)庫(kù)連接實(shí)例 / 總是返回一個(gè)新的連接 $connection = $this->_di->get('db'); public function someOtherDbTask() / 獲得共享連接實(shí)例 / 每次請(qǐng)求都返回相同的連接實(shí)例 $connection = $this->_di->getShared('db'); / 這個(gè)方法也需要一個(gè)輸入過(guò)濾的依賴服務(wù) $filter = $this
17、->_di->get('filter'); $di = new PhalconDI();/在容器中注冊(cè)一個(gè)db服務(wù)$di->set('db', function() return new Connection(array( "host" => "localhost", "username" => "root", "password" => "secret", "dbname" =>
18、"invo" ););/在容器中注冊(cè)一個(gè)filter服務(wù)$di->set('filter', function() return new Filter(););/在容器中注冊(cè)一個(gè)session服務(wù)$di->set('session', function() return new Session(););/把傳遞服務(wù)的容器作為唯一參數(shù)傳遞給組件$some = new SomeComponent($di);$some->someTask();這個(gè)組件現(xiàn)在可以很簡(jiǎn)單的獲取到它所需要的服務(wù),服務(wù)采用延遲加載的方式,只有在需要使用的時(shí)
19、候才初始化,這也節(jié)省了服務(wù)器資源。這個(gè)組件現(xiàn)在是高度解耦。例如,我們可以替換掉創(chuàng)建連接的方式,它們的行為或它們的任何其他方面,也不會(huì)影響該組件。實(shí)現(xiàn)方法(Our approach)¶PhalconDI 是一個(gè)實(shí)現(xiàn)依賴注入和定位服務(wù)的組件,而且它本身就是一個(gè)裝載它們的容器。因?yàn)镻halcon是高度解構(gòu)的,整合框架的不同組件,使用PhalconDI是必不可少的。開(kāi)發(fā)者也可以使用這個(gè)組件去注入依賴和管理的應(yīng)用程序中來(lái)自不同類的全局實(shí)例?;旧希@個(gè)組件實(shí)現(xiàn)了 控制反轉(zhuǎn)(/wiki/%E6%8E%A7%E5%88%B6%E5%8F%8D%E8%BD
20、%AC) 的模式。使用這種模式,組件的對(duì)象不用再使用setter或者構(gòu)造函數(shù)去接受依賴實(shí)例,而是使用請(qǐng)求服務(wù)的依賴注入。這減少了總的復(fù)雜性,因?yàn)樵诮M件內(nèi),只有一個(gè)方法去獲取所需的依賴實(shí)例。另外,該模式增加了代碼的可測(cè)試性,從而使其不易出錯(cuò)。使用容器注冊(cè)服務(wù)(Registering services in the Container)¶框架本身或者開(kāi)發(fā)者都可以注冊(cè)服務(wù)。當(dāng)一個(gè)組件A需要組件B(或者它的類的實(shí)例) 去操作,它可以通過(guò)容器去請(qǐng)求組件B,而不是創(chuàng)建一個(gè)新的組件B實(shí)例。這個(gè)工作方法給我們提供了許多優(yōu)勢(shì):· 我們可以很容易的使用一個(gè)我們自己建立的或者是第三方的組件去替換
21、原有的組件。· 我們完全控制對(duì)象的初始化,這讓我們?cè)趥鬟f它們的實(shí)例到組件之前,根據(jù)需要設(shè)置這些對(duì)象。· 我們可以在一個(gè)結(jié)構(gòu)化的和統(tǒng)一組件內(nèi)獲取全局實(shí)例。服務(wù)可以使用不同方式去定義:<?php/ 創(chuàng)建一個(gè)依賴注入容器$di = new PhalconDI();/ 通過(guò)類名稱設(shè)置服務(wù)$di->set("request", 'PhalconHttpRequest');/ 使用匿名函數(shù)去設(shè)置服務(wù),這個(gè)實(shí)例將被延遲加載$di->set("request", function() return new Phalc
22、onHttpRequest(););/ 直接注冊(cè)一個(gè)實(shí)例$di->set("request", new PhalconHttpRequest();/ 使用數(shù)組方式定義服務(wù)$di->set("request", array( "className" => 'PhalconHttpRequest');使用數(shù)組的方式去注冊(cè)服務(wù)也是可以的:<?php/ 創(chuàng)建一個(gè)依賴注入容器$di = new PhalconDI();/ 通過(guò)類名稱設(shè)置服務(wù)$di"request" = 'Pha
23、lconHttpRequest'/ 使用匿名函數(shù)去設(shè)置服務(wù),這個(gè)實(shí)例將被延遲加載$di"request" = function() return new PhalconHttpRequest();/ 直接注冊(cè)一個(gè)實(shí)例$di"request" = new PhalconHttpRequest();/ 使用數(shù)組方式定義服務(wù)$di"request" = array( "className" => 'PhalconHttpRequest');在上面的例子中,當(dāng)框架需要訪問(wèn)request服務(wù)的內(nèi)
24、容,它會(huì)在容器里面查找名為request的服務(wù)。 在容器中將返回所需要的服務(wù)的實(shí)例。當(dāng)有需要時(shí),開(kāi)發(fā)者可能最終需要替換這個(gè)組件。每個(gè)方法(在上面的例子證明)用于設(shè)置/注冊(cè)服務(wù)方面具都具有優(yōu)勢(shì)和劣勢(shì)。這是由開(kāi)發(fā)者和特別的要求決定具體使用哪個(gè)。通過(guò)字符串設(shè)置一個(gè)服務(wù)是很簡(jiǎn)單,但是缺乏靈活性。通過(guò)數(shù)組設(shè)置服務(wù)提供了更加靈活的方式,但是使代碼更復(fù)雜。匿名函數(shù)是上述兩者之間的一個(gè)很好的平衡,但是會(huì)導(dǎo)致比預(yù)期的更多維護(hù)。PhalconDI 對(duì)每個(gè)儲(chǔ)存的服務(wù)提供了延遲加載。除非開(kāi)發(fā)者選擇直接實(shí)例化一個(gè)對(duì)象并將其存儲(chǔ)在容器中,任何儲(chǔ)存在里面的對(duì)象(通過(guò)數(shù)組,字符串等等設(shè)置的)都將延遲加載,即只要當(dāng)使用到時(shí)才實(shí)
25、例化。簡(jiǎn)單的注冊(cè)(Simple Registration)¶就像你之前看到的那樣,這里有幾種方法去注冊(cè)服務(wù)。下面是簡(jiǎn)單調(diào)用的例子:字符串(String)¶使用字符串注冊(cè)服務(wù)需要一個(gè)有效的類名稱,它將返回指定的類對(duì)象,如果類還沒(méi)有加載的話,將使用自動(dòng)加載器實(shí)例化對(duì)象。這種類型不允許向構(gòu)造函數(shù)指定參數(shù):<?php/ 返回 new PhalconHttpRequest(); 對(duì)象$di->set('request', 'PhalconHttpRequest');對(duì)象(Object)¶這種類型注冊(cè)服務(wù)需要一個(gè)對(duì)象。實(shí)際上,這個(gè)服
26、務(wù)不再需要初始化,因?yàn)樗呀?jīng)是一個(gè)對(duì)象,可以說(shuō),這是不是一個(gè)真正的依賴注入,但是如果你想強(qiáng)制總是返回相同的對(duì)象/值,使用這種方式還是有用的:<?php/ 返回 PhalconHttpRequest(); 對(duì)象$di->set('request', new PhalconHttpRequest();閉包與匿名函數(shù)(Closures/Anonymous functions)¶這個(gè)方法提供了更加自由的方式去注冊(cè)依賴,但是如果你想從外部改變實(shí)例化的參數(shù)而不用改變注冊(cè)服務(wù)的代碼,這是很困難的:<?php$di->set("db", f
27、unction() return new PhalconDbAdapterPdoMysql(array( "host" => "localhost", "username" => "root", "password" => "secret", "dbname" => "blog" ););這些限制是可以克服的,通過(guò)傳遞額外的變量到閉包函數(shù)里面:<?php/ 把當(dāng)前域的$config變量傳遞給匿名函數(shù)使用$d
28、i->set("db", function() use ($config) return new PhalconDbAdapterPdoMysql(array( "host" => $config->host, "username" => $config->username, "password" => $config->password, "dbname" => $config->name ););復(fù)雜的注冊(cè)(Complex Registr
29、ation)¶如果要求不用實(shí)例化/解析服務(wù),就可以改變定義服務(wù)的話,我們需要使用數(shù)組的方式去定義服務(wù)。使用數(shù)組去定義服務(wù)可以更加詳細(xì):<?php/ 通過(guò)類名和參數(shù),注冊(cè)logger服務(wù)$di->set('logger', array( 'className' => 'PhalconLoggerAdapterFile', 'arguments' => array( array( 'type' => 'parameter', 'value' =&g
30、t; './apps/logs/error.log' ) );/ 使用匿名函數(shù)的方式$di->set('logger', function() return new PhalconLoggerAdapterFile('./apps/logs/error.log'););上面兩種注冊(cè)服務(wù)的方式的結(jié)果是一樣的。然而,使用數(shù)組定義的話,在需要的時(shí)候可以變更注冊(cè)服務(wù)的參數(shù):<?php/ 改變logger服務(wù)的類名$di->getService('logger')->setClassName('MyCusto
31、mLogger');/ 不用實(shí)例化就可以改變第一個(gè)參數(shù)值$di->getService('logger')->setParameter(0, array( 'type' => 'parameter', 'value' => './apps/logs/error.log');除了使用數(shù)組的語(yǔ)法注冊(cè)服務(wù),你還可以使用以下三種類型的依賴注入:構(gòu)造函數(shù)注入(Constructor Injection)¶這個(gè)注入方式是通過(guò)傳遞依賴/參數(shù)到類的構(gòu)造函數(shù)。讓我們假設(shè)我們有下面的組件:<?phpnamespace SomeApp;use PhalconHttpResponse;class Some
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 西北大學(xué)《口腔醫(yī)學(xué)臨床前技能訓(xùn)練(二)》2023-2024學(xué)年第二學(xué)期期末試卷
- 浙江汽車職業(yè)技術(shù)學(xué)院《數(shù)據(jù)分析與應(yīng)用》2023-2024學(xué)年第二學(xué)期期末試卷
- 青島黃海學(xué)院《入侵檢測(cè)與入侵防御》2023-2024學(xué)年第二學(xué)期期末試卷
- 山東勝利職業(yè)學(xué)院《供應(yīng)鏈與物流概論》2023-2024學(xué)年第二學(xué)期期末試卷
- 甘肅省定西市漳縣2025屆五年級(jí)數(shù)學(xué)第二學(xué)期期末統(tǒng)考模擬試題含答案
- 廣東茂名健康職業(yè)學(xué)院《高等藥理學(xué)(雙語(yǔ))》2023-2024學(xué)年第二學(xué)期期末試卷
- 古希臘哲學(xué)和中國(guó)哲學(xué)
- 功放高頻移相
- 公共交通服務(wù)規(guī)范管理制度
- 工程項(xiàng)目成本控制的原則
- 火災(zāi)自動(dòng)報(bào)警系統(tǒng)施工過(guò)程檢查記錄
- TD/T 1069-2022 國(guó)土空間生態(tài)保護(hù)修復(fù)工程驗(yàn)收規(guī)范(正式版)
- 踝關(guān)節(jié)扭傷康復(fù)治療
- FZ∕T 01085-2018 粘合襯剝離強(qiáng)力試驗(yàn)方法
- 白龍江引水工程環(huán)境影響報(bào)告書(shū)(公示版)
- 《短視頻拍攝與制作》課件-3短視頻中期拍攝
- 瀏陽(yáng)煙花術(shù)語(yǔ)大全
- 居民心理健康知識(shí)講座課件
- 2024年英語(yǔ)專業(yè)四級(jí)考試真題及詳細(xì)答案
- 成語(yǔ)故事葉公好龍
- MHT:中小學(xué)生心理健康檢測(cè)(含量表與評(píng)分說(shuō)明)
評(píng)論
0/150
提交評(píng)論