核心要點
在應用程序開發中,我們嘗試創建獨立的模塊,以便在未來的項目中重用代碼。但是,創建提供有用功能的完全獨立的模塊很困難;除非正確管理它們的依賴關係,否則它們可能會導致維護噩夢。這就是依賴注入派上用場的地方,因為它使我們能夠注入代碼正常運行所需的依賴項,而無需將它們硬編碼到模塊中。 Pimple是一個簡單的依賴注入容器,它利用PHP的閉包以可管理的方式定義依賴項。在本文中,我們將探討硬編碼依賴項的問題,依賴注入如何解決這些問題,以及如何使用Pimple來使利用依賴注入的代碼更易於維護。
具體依賴項的問題
在編寫應用程序時,我們會使用許多PHP類。一個類可能需要調用一個或多個其他類的方法來提供預期的功能,因此我們說第一個類依賴於其他類。例如:
<?php class A { public function a1() { $b = new B(); $b->b1(); } }
類A依賴於類B。如果類B不可用,則上述代碼將無法工作。此外,每次我們在類中硬編碼對象的創建時,我們都會對該類產生具體的依賴關係。具體依賴關係是編寫可測試代碼的障礙。更好的方法是向類A提供類B的對象。這些對象可以通過A的構造函數或setter方法提供。在我們進一步討論之前,讓我們來看一個更現實的場景。
如今,在社交網絡網站上共享內容非常普遍,大多數網站都在其網站上直接顯示其社交資料提要。假設我們有一個名為SocialFeeds的類,它從Twitter、Facebook、Google 等社交網站生成提要。創建單獨的類來處理這些服務中的每一個。在這裡,我們將研究與Twitter交互的類TwitterService。 SocialFeeds類使用TwitterService請求Twitter提要。 TwitterService與數據庫交互以檢索訪問API的特定用戶令牌。令牌傳遞給OAuth類,該類使用提供的令牌檢索提要並將其返回給SocialFeeds類。
<?php class A { public function a1() { $b = new B(); $b->b1(); } }
<?php class SocialFeeds { public function getSocialFeeds() { $twService = new TwitterService(); echo $twService->getTweets(); } }
<?php class TwitterService { public function getTweets() { $db = new DB(); $query = "Query to get user token from database"; $token = $db->getQueryResults($query); $oauth = new OAuth(); return $oauth->requestTwitterFeed($token); } }
<?php class OAuth { public function requestTwitterFeed($token) { // Retrieve and return twitter feed using the token } }
很明顯,SocialFeeds依賴於TwitterService。但是TwitterService依賴於DB和OAuth,因此SocialFeeds間接依賴於DB和OAuth。那麼問題是什麼呢? SocialFeeds依賴於三個類的具體實現,因此不可能在沒有其他類的真實實現的情況下單獨測試SocialFeeds。或者,假設我們想使用不同的數據庫或不同的OAuth提供程序。在這種情況下,我們必須在整個代碼中用新類替換現有類。
修復具體依賴項
解決這些依賴項問題的方案很簡單,即在必要時動態提供對象,而無需使用具體實現。有兩種類型的技術可以注入依賴項:基於構造函數的依賴注入和基於設置器的注入。
基於構造函數的注入
使用基於構造函數的依賴注入,依賴對像是在外部創建的,並作為參數傳遞給類的構造函數。我們可以將這些對象分配給類變量,並在類內任何地方使用。 SocialFeeds類的基於構造函數的注入如下所示:
<?php class DB { public function getQueryResults($query) { // Get results from database and return token } }
TwitterService的實例作為對像傳遞給構造函數。 SocialFeeds仍然依賴於TwitterService,但現在我們可以自由地提供不同版本的Twitter服務提供程序,甚至可以提供用於測試目的的模擬對象。關於TwitterService,DB和OAuth類也以類似的方式定義。
<?php class SocialFeeds { public $twService; public function __construct($twService) { $this->twService = $twService; } public function getSocialFeeds() { echo $this->twService->getTweets(); } }
基於設置器的注入
使用基於設置器的注入,對像是通過setter方法而不是構造函數提供的。以下是SocialFeeds類的基於設置器的依賴注入實現:
<?php $db = new DB(); $oauth = new OAuth(); $twService = new TwitterService($db, $oauth); $socialFeeds = new SocialFeeds($twService); $socialFeeds->getSocialFeeds();
現在包括DB和OAuth的初始化代碼如下所示:
<?php class SocialFeeds { public $twService; public function getSocialFeeds() { echo $this->twService->getTweets(); } public function setTwitterService($twService) { $this->twService = $twService; } }
構造函數與設置器注入
選擇基於構造函數的注入還是基於設置器的注入取決於您。當需要所有依賴項才能實例化類時,基於構造函數的注入是合適的。當並非每次都需要依賴項時,基於設置器的注入是合適的。
優點
缺點
了解了依賴注入和各種注入技術後,是時候看看Pimple以及它如何融入其中了。
Pimple在DI中的作用
當我們已經可以使用前面提到的技術注入依賴項時,您可能想知道為什麼需要Pimple。要回答這個問題,我們需要看看DRY原則。
不要重複自己(DRY)是軟件開發的一個原則,旨在減少各種信息的重複,這在多層架構中特別有用。 DRY原則的陳述是“每個知識片段都必須在一個系統中具有單個、明確、權威的表示”——維基百科
考慮基於構造函數的注入示例。每次我們想要SocialFeed類的對象時,我們都必須重複實例化和傳遞其依賴項的整個設置過程。根據DRY,應避免此類代碼以防止維護出現問題。 Pimple充當定義此類依賴項以避免重複的容器。讓我們來看一個簡單的例子,看看Pimple是如何工作的。
<?php class A { public function a1() { $b = new B(); $b->b1(); } }
創建Pimple的實例充當存儲依賴項的容器。它實現SPL ArrayAccess接口,因此使用它與使用數組非常相似。首先,我們定義了一個鍵,該鍵保存我們想要的某個任意類的名稱。然後,我們定義了一個閉包來返回指定類的實例,該實例充當服務。請注意,將向$c傳遞容器的實例,因此我們可以根據需要引用其他已定義的鍵;每個已定義的參數或對像都可通過$c變量在閉包中使用。現在,每當我們想要類的實例時,我們都可以引用鍵來檢索對象。讓我們將SocialFeeds示例轉換為Pimple。 Pimple官方網站上的示例顯示了基於構造函數的注入,因此在這裡我們將說明基於設置器的注入。請記住,為了使用Pimple,我們不需要修改前面定義的任何setter方法或代碼——我們只是封裝了邏輯。
<?php class SocialFeeds { public function getSocialFeeds() { $twService = new TwitterService(); echo $twService->getTweets(); } }
DB和OAuth類都是獨立的模塊,因此我們直接在閉包內返回它們的新的實例。然後,我們使用基於設置器的注入向TwitterService類添加依賴項。我們已經將DB和OAuth類添加到容器中,因此我們可以使用$c['db']和$c['oauth']直接在函數內訪問它們。現在,依賴項作為服務封裝在容器內。每當我們想要使用不同的DB類或不同的OAuth服務時,我們只需替換容器語句中的類,一切都會完美運行。使用Pimple,您只需要在一個地方添加新的依賴項。
高級Pimple用法
在上述場景中,Pimple會在每次請求時從閉包返回每個類的新的實例。在某些情況下,我們需要使用相同的對象而無需每次都初始化新的實例,例如連接到數據庫就是一個完美的例子。 Pimple提供了使用共享對象返回相同實例的能力,這樣做需要我們通過share()方法指定閉包,如下所示:
<?php class A { public function a1() { $b = new B(); $b->b1(); } }
此外,到目前為止,我們已經在Pimple容器中的單個位置定義了所有依賴項。但是,考慮一下我們需要具有其依賴項的服務,但配置方式與原始服務略有不同的情況。例如,假設我們需要訪問ORM來實現TwitterService類的某些功能。我們不能更改現有的閉包,因為它會強制所有現有功能使用ORM。 Pimple提供extend()方法來動態修改現有閉包,而不會影響原始實現。考慮以下代碼:
<?php class SocialFeeds { public function getSocialFeeds() { $twService = new TwitterService(); echo $twService->getTweets(); } }
現在,我們能夠在特殊情況下使用tweet_service的不同擴展版本。第一個參數是服務的名稱,第二個參數是一個函數,該函數可以訪問對象實例和容器。實際上,extend()是動態添加依賴項以適應不同情況的強大方法,但請確保將服務的擴展版本限制在最低限度,因為它會增加重複代碼的數量。
總結
管理依賴項是Web應用程序開發中最重要和最困難的任務之一。我們可以使用構造函數和setter方法的依賴注入來有效地管理它們。但是,依賴注入本身也有一些麻煩,Pimple通過提供一個輕量級容器來以DRY的方式創建和存儲對象依賴項來解決這些問題。請隨時在下面的評論中分享您在項目中管理依賴項的經驗,以及您對Pimple作為依賴注入容器的看法。
關於使用Pimple進行依賴注入的常見問題解答 (FAQ)
Pimple是一個簡單的PHP依賴注入容器,允許您管理和集中應用程序中的服務。它用於PHP,使代碼更靈活、更可重用和更易於測試。通過使用Pimple,您可以在一個地方實例化對象,然後將它們注入到應用程序的不同部分,從而減少對全局狀態的需求,並使您的代碼更易於維護和測試。
Pimple通過在容器中存儲服務定義來工作。這些定義是可以調用(函數或方法)的,它們返回服務的實例。當您從容器訪問服務時,Pimple會執行服務定義以創建服務對象。這允許您以集中方式管理服務,並在整個應用程序中共享服務。
可以使用Composer(PHP的依賴項管理工具)安裝Pimple。您可以在系統上全局安裝Composer,然後通過運行命令composer require pimple/pimple
在項目中引入Pimple。
在Pimple中,您可以通過將可調用對象分配給容器中的鍵來定義服務。可調用對象應返回服務的實例。例如,您可以像這樣為郵件發送器類定義服務:
$container['mailer'] = function ($c) { return new Mailer($c['smtp']); };
在此示例中,郵件發送器服務定義為Mailer類的新的實例,其中smtp服務作為依賴項注入。
您可以使用帶有服務鍵的數組表示法來訪問Pimple中的服務。例如,您可以像這樣訪問郵件發送器服務:$mailer = $container['mailer'];
。當您訪問服務時,Pimple會執行服務定義並返回服務對象。
默認情況下,Pimple每次訪問服務時都會返回服務的新的實例。如果您想共享服務並每次返回相同的實例,可以使用share()
方法。例如,您可以像這樣共享郵件發送器服務:$container['mailer'] = $container->share(function ($c) { return new Mailer($c['smtp']); });
。
是的,您可以使用extend()
方法擴展Pimple中的服務。這允許您在定義服務後修改它。例如,您可以像這樣擴展郵件發送器服務以添加其他配置:
$container['mailer'] = $container->extend('mailer', function ($mailer, $c) { $mailer->setFrom($c['email.from']); return $mailer; });
在此示例中,setFrom()
方法使用email.from
服務作為參數在郵件發送器服務上調用。
在Pimple中,您可以使用protect()
方法保護參數(不應被視為服務的參數)。這允許您在容器中存儲值,而不會將它們視為服務定義。例如,您可以像這樣保護配置值:$container['config.value'] = $container->protect(function () { return 'value'; });
。
您可以通過創建PimpleContainer類的新的實例並在其中定義服務來在一個項目中使用Pimple。然後,您可以在應用程序中需要的地方從容器訪問服務。這允許您以集中方式管理服務並將它們注入到應用程序的不同部分。
Pimple為PHP開發提供了許多好處。它使您的代碼更靈活,因為它允許您以集中方式管理服務。它使您的代碼更易於重用,因為它允許您在整個應用程序中共享服務。它使您的代碼更易於測試,因為它允許您注入模擬服務進行測試。通過使用Pimple,您可以提高代碼質量,並使其更易於維護和測試。
以上是PHP主|用丘疹注入依賴的詳細內容。更多資訊請關注PHP中文網其他相關文章!