簡體   English   中英

這是構造函數注入的明智實現嗎?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM