2014-10-10 66 views
6

我終於開始在PHP中探索特性。我認爲我會嘗試的第一個地方是將配置位注入類中。如果我使用DIC,我可能有這樣的代碼在需要配置對象的任何類:如何避免用於依賴注入的衝突PHP特性

protected function SetConfig($config) { 
    $this->config = $config; 
} 

protected $config; 

這似乎是一個天然的適合特質,以避免所有的地方是樣板代碼,所以我可能會造成這樣的:

trait Config { 
    protected function SetConfig($config) { 
     $this->config = $config; 
    } 

    protected $config; 
} 

,然後用它像這樣:

class Foo { 
    use Config; 

    public function __construct() { 
     //can now use $this->config 
    } 
} 

這是偉大的。現在,讓我們說,我想創建第二個特點,比如說,對日誌記錄:

trait Logger { 
    protected function SetLogger($logger) { 
     $this->logger = $logger; 
    } 

    protected $logger; 
} 

,我可以使用這樣的:

class Foo { 
    use Logger; 

    public function __construct() { 
     //can now use $this->logger 
    } 
} 

也很大。現在,如果這兩個特徵想要互相使用,問題就來了。它似乎很合理的一個記錄器類需要有注入的配置對象,這意味着這樣做:

trait Logger { 
    use Config; 

    protected function SetLogger($logger) { 
     $this->logger = $logger; 
    } 

    protected $logger; 
} 

但接下來的事情會打破當其他類使用這兩種特質:

class Foo { 
    use Config, Logger; 

    public function __construct() { 
     //want to use $this->config and $this->logger 
    } 
} 

這當然不起作用,因爲配置位在Foo中被有效地複製。

我可以略去Logger trait中的use Config;作品,知道它最終會在那裏。但是這對我來說很奇怪,因爲它會產生一種外部依賴性。如果我想將記錄器用於某個尚未具有配置特性的地方,該怎麼辦?這個解決方案也意味着我需要忍受我的IDE(PhpStorm 8)警告我關於未知方法,而不是提供自動完成。我意識到我可以通過使用@method反過來解決這些問題,但這只是把口紅放在豬身上,可以這麼說。

我也可以在記錄器中別名配置位,但這也有問題。

所有這一切都有點氣味給它的,但我還沒有想出這是否是因爲這對我來說是一個新的模式,或者如果它確實是一個臭模式。無論哪種方式,我都不確定實現這種方法的最佳方式是否可行。

上解決這一特質問題的最好方法有什麼建議?還是避免DIC捷徑的特點是更好的?

+0

從未使用過'traits'爲DI自己,但一我可以提出的建議是,如果你有需要其他特性的特質,你可以將它們轉換成類來獲益。 – Crackertastic 2014-10-10 20:41:20

+0

記錄器特徵可能需要配置才能知道要使用的日誌級別或寫入日誌文件的位置。但是,真的,問題不僅僅是這種情況 - 這只是一個例子。更廣泛地說,在覈心功能之間有依賴關係並不罕見。在每個班級中使用樣板代碼(屬性和設置器)時,這不是問題,但這是特性的問題。在這種情況下,將'使用配置'添加到記錄器將不起作用,因爲同時使用記錄器和配置(上面的,Foo)的類將生成致命錯誤,因爲來自配置的部分是重複的。 – 2014-10-10 21:18:30

+1

這就是我問這個問題的原因。正如我所說,它有一種有趣的氣味,但同時,性狀似乎是解決DI中固有的樣板問題的一種很好的方法。那麼將兩者結合在一起的最佳方法是什麼? – 2014-10-10 21:46:18

回答

3

我發現有用的方法是使用getters以及setters。這可以讓你要求一個特定的吸氣劑存在,而不與其他特性相沖突。

trait Config { 
    protected function SetConfig($config) { 
     $this->config = $config; 
    } 

    protected function GetConfig() { 
     return $this->config; 
    } 

    protected $config; 
} 

trait Logger { 
    abstract protected function GetConfig(); 

    protected function SetLogger($logger) { 
     $this->logger = $logger; 
    } 

    protected $logger; 
} 

class Baz { 
    use Config, Logger; 

    // ... 

} 

在Baz中,Config特徵提供了所需的抽象方法,而Baz的構成沒有錯誤。如果錯誤地只使用記錄儀,你會得到一個致命錯誤:類巴茲包含1種抽象方法,因此必須聲明爲抽象或實現其餘的方法(巴茲:: GetConfig)

+0

這確實完成了工作,但它並不理想。主要的問題是:1)文檔變得混亂(例如,如果只看到Logger,怎麼知道GetConfig()應該來自哪裏?)2)必須使用Config來使用Logger,這是一個奇怪的外部依賴,並且難以記錄,3)這也涉及到文檔,但是一個(* can * one?)如何用PHPDoc正確註釋它,以便IDE代碼完成和重構工作?要完成這項工作的解決方案的要點,但擁有一個真正乾淨的方式來完成這項工作真是太棒了。 – 2014-10-20 23:37:34

+1

作爲後續工作,我意識到保留1和3可能會在phpDocumentor更新中解決,以便更好地處理特徵,如果事實上它們沒有被解決。但我沒有看到一個解決方案來到2.明確要求客戶端類如果使用Logger的話必須知道使用Config將會變得混亂,不管怎麼樣,我想。我可以看到它在一些明確定義的情況下運行良好,但在這種情況下,Config和Logger只是覺得它們應該是可重用的「磚塊」。然後再次,如果不太可能一個班級將使用記錄器,但不會配置.... – 2014-10-20 23:59:15

+0

你知道任何在特徵之間流動的IDE嗎?當我點擊$ this-> getConfig時,IDE進入抽象定義,而不是trait Config中的方法定義。也許可以幫助IDE做到這一點的phpdoc? – corretge 2015-06-26 08:15:24