[英]Is this a sane implementation of constructor injection?
在我對服務定位器提出問題之后, 我決定改用構造函數注入。 請考慮以下代碼:
<?php
interface IAppServiceRegistry {
public function getDb();
public function getLogger();
}
interface IFooServiceRegistry extends IAppServiceRegistry {
public function getFooBarBazModel();
}
class AppServiceRegistry
implements IAppServiceRegistry, IFooServiceRegistry
{
private $logger;
private $db;
private $fooBarBazModel;
public function getDb() {
// return db (instantiate if first call)
}
public function getLogger() {
// return logger (instantiate if first call)
}
public function getFooBarBazModel() {
if (!isset($this->fooBarBazModel)) {
$this->fooBarBazModel = new FooBarBazModel( $this->getDb() );
}
return $this->fooBarBazModel;
}
}
// Example client classes:
/**
* Depends on db, logger and foomodel.
*/
class Foo {
private $db;
private $logger;
private $fooModel;
public function __construct(IFooServiceRegistry $services) {
$this->db = $services->getDb();
$this->logger = $services->getLogger();
$this->fooModel = $services->getFooModel();
}
}
/**
* Depends on only db and logger.
*/
class BarBaz {
private $db;
private $logger;
public function __construct(IAppServiceRegistry $services) {
$this->db = $services->getDb();
$this->logger = $services->getLogger();
}
}
然后,隨着應用程序的發展,我將在注冊表中添加新的服務工廠方法,並在邏輯上適當的地方創建隔離的接口。
這種方法理智嗎?
盡管我通常不閱讀php,但我認為我大部分都可以理解。 從技術上講,它看起來不錯,但隨后您編寫
隨着應用程序的發展,我將向注冊表添加新的服務工廠方法
這往往會損害松散耦合的想法,因為您現在有了專用的服務定位器,而不是通用服務定位器。
向每個類中注入這樣的服務注冊表違反了單一職責原則 (SRP),因為一旦類可以訪問注冊表,就很難請求比最初設想的又一個依賴,並且最終將得到God Object 。
最好將所需的依賴項直接注入需要它們的類中。 因此,您的Foo類的構造函數應采用db,記錄器和fooModel,而BarBaz類應僅采用db和記錄器參數。
接下來的問題可能是:如果我需要很多不同的依賴項來執行工作該怎么辦? 這將需要一個帶有許多參數的真正丑陋的構造函數,這與其他著名的OO做法背道而馳。
的確如此,但是如果您需要大量依賴關系,則可能違反了SRP,應嘗試將設計拆分為更細粒度的對象:)
此實現與您先前向我們展示的服務定位器幾乎相同。
要問的一個好問題是,在查看對象的類時,是否了解對象的所有事項才能完成其工作。 就您而言,您仍然不知道。
如果Foo需要數據庫,記錄器和模型,則可以通過在構造函數中進行查詢來弄清楚這一點。
這是一本很好的文章:
http://misko.hevery.com/code-reviewers-guide/flaw-digging-into-collaborators/
我最近一直在努力解決這種問題。 服務定位器與摻雜注入。
我同意Mark的觀點,方法是根據需要將單個細粒度對象注入構造函數。 正如Mark所強調的那樣,唯一的缺點是,當您構建復雜的對象圖時,不可避免地必須從某處開始。 這意味着您的高級對象將注入許多服務(對象)。
解決此問題的簡單方法是使用某些方法為您完成艱苦的工作。 最好的例子是Google的Guice,在我看來,這是處理事情的一種非常好的方法。 可悲的是它是為Java編寫的! 有關於的PHP版本; 我不確定在這一點上他們中的任何一個都能達到Guice的標准。
我已經寫了一篇有關該主題的文章,其中有更詳細的內容。 您可能會發現有趣。 它包括依賴項注入框架的簡單實現。
底線是,如果您有一個具有許多要求的Foo類,則可以這樣創建類:
/**
* Depends on db, logger and foomodel.
*/
class Foo
{
private $db;
private $logger;
private $fooModel;
/**
* (other documentation here)
* @inject
*/
public function __construct(IDbService $db, ILoggerService $logger, $iModelService $model)
{
// do something
}
}
當您想要一個新的Foo對象時,您只需詢問依賴注入框架即可創建一個 :
$foo = $serviceInjector->getInstance('Foo');
依賴項注入器將進行艱苦的工作,確保注入了依賴項。 如果可以的話,這包括依賴關系的任何依賴關系。 換句話說,它將以遞歸方式將其整理到樹上。
稍后,當您發現需要IBarService對象時,可以將其添加到Construtor中,而無需更改任何其他代碼!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.