2009-12-14 23 views
1

my question on service locators開始,我決定改用構造函數注入。請考慮下面的代碼:這是構造器注入的一個理智的實現嗎?

<?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(); 
     } 
    } 

然後我會添加新的服務工廠的方法來對註冊表的應用演變,只要創造適當的邏輯分離的接口。

這種方法是否理智?

回答

4

儘管我通常不會讀取php,但我想我理解它的大部分。從技術上講,它看起來不錯,但你寫

我會添加新的服務工廠的方法來對註冊表的應用演變

這往往是因爲不是一般的傷害鬆耦合的想法目的服務定位器你現在有一個專門的服務定位器。

注射這種serivce註冊表到每個類的工作對Single Responsibility Principle(SRP),因爲一旦一個類可以訪問註冊表,實在是太容易申請又一依賴比原來設想的,你會最終有一個God Object

將所需的依賴項直接注入需要它們的類中會更好。因此,你的Foo類的構造函數應該有一個db,一個記錄器和一個fooModel,而BarBaz類應該只帶一個db和一個記錄器參數。

接下來的問題可能是:如果我需要很多不同的依賴來執行這項工作,該怎麼辦?這需要一個真正醜陋的構造函數,它有很多參數,這與其他衆所周知的OO實踐相反。

沒錯,但如果你需要大量的相關性,你可能違反了SRP,並應儘量設計分割成更細粒度的對象:)

+0

我感覺有些東西聞起來很糟糕,但是讓我沿着這條路走下去的是「什麼時候課程取決於5或6種不同的模型,再加上記錄器等?」。但是我應該記住1.YAGNI和2.正如你所指出的,也許我的設計需要再次關注這一點,可能會違反SRP。謝謝您的幫助! – oops

+0

@MarkSeemann如何將ServicesProvider objet作爲單例讓您可以訪問2個或3個服務對象?它在哪裏破壞SRP?我的意思是ServicesProvider做一件事就是提供服務對象。 – Rushino

+0

當談到DI時,單身人士相當成問題:http://stackoverflow.com/questions/1917180/di-and-singleton-pattern-in-one-implementation/1917230#1917230 –

1

這個實現是幾乎一樣的服務定位器,你以前給我們看。

要問的一個好問題是,在查看對象的類時,你知道對象的所有信息都需要完成工作。在你的情況下,你仍然不知道。

如果Foo需要db,logger和model,可以通過在構造函數中詢問這些來清除它。

這裏對此事進行了良好的閱讀:

http://misko.hevery.com/code-reviewers-guide/flaw-digging-into-collaborators/

+0

感謝您的回答和鏈接,koen !我會投票給你,但我似乎沒有特權。 – oops

+0

好的,我現在可以投票..你去! – oops

1

我一直是這樣那樣的問題最近拼殺。服務定位器與傾向注入。

我同意Mark的說法,即按需要將各個細粒度對象注入構造函數。正如Mark強調的那樣,唯一的缺點是當您構建複雜的對象圖時,您不可避免地必須在的某處開始。這意味着您的高級對象將會注入服務(對象)的批次

這個簡單的方法是使用一些東西來爲你做辛苦的工作。其中最好的例子就是谷歌的Guice,在我看來,這是一種很好的解決問題的方式。可悲的是它是爲Java編寫的!有關於PHP的版本;在這一點上,我不確定他們中的任何一個是否符合Guice。

我已經寫了一個post on this topic這更詳細;您可能會感興趣。它包含了一個簡單的依賴注入框架的實現

的底線是,如果你已經有了類有一些要求,你可以創建你的類這樣:

/** 
* 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 = $serviceInjector->getInstance('Foo'); 

依賴注入會做的辛勤工作,希確保它注入依賴關係。這包括依賴關係的任何依賴關係,如果有意義的話。換句話說,它會將它全部遞歸排列在樹上。後來,當您發現您需要一個IBarService對象時,您可以將其添加到Construtor中,而不更改任何其他代碼!

相關問題