2014-06-12 61 views
0

因此,Drupal使用一個基於Symfony2的依賴注入容器(DIC)來組織它的服務。依賴注入容器(DIC) - 如何處理過時的服務?

此外,我喜歡自己使用這種模式(使用更簡單和手工製作的解決方案)用於較小的項目。

簡化,它看起來像這樣:

class Container { 
    private $services = array(); 
    function getService($key) { 
    if (isset($this->services[$key])) { 
     return $this->services[$key]; 
    } 
    $method = 'create_' . $key; 
    // @todo Check if method exists. 
    // Call the method to lazy-create the service. 
    return $this->services[$key] = $this->$method($key); 
    } 

    function create_kitchen() { 
    // Kitchen depends on Stove. 
    $stove = $this->getService('stove'); 
    return new Kitchen($stove); 
    } 

    function create_stove() { 
    return new Stove(); 
    } 
} 

$container = new Container(); 
$kitchen = $container->getService('kitchen'); 

到目前爲止好。
但是如果我想用新的更換爐竈而不更換廚房呢?

$kitchen = $container->getService('kitchen'); 
$kitchen->cookAnEgg(); 
$container->replace('stove', new BetterStove()); 
$kitchen->cookAnEgg(); 

我需要一個機制來替換廚房爲好,讓老廚實例變得過時,或者我需要讓廚房知道爐子已被取代,因此第二個雞蛋可以用熟新的爐子。

如果廚房想要自己更換爐竈怎麼辦?

class Kitchen { 
    private $stove; 
    private $electrician; 
    function __construct(Stove $stove, Electrician $electrician) { 
    $this->stove = $stove; 
    $this->electrician = $electrician; 
    } 
    function cookAnEgg() { 
    while ($this->stove->isBroken()) { 
     $this->electrician->installNewStove(); 
    } 
    .. 
    } 
} 

廚房是怎麼知道新爐子的?

有沒有處理這種情況的最佳實踐?

我認爲使用觀察者模式的,但什麼是做,在組合與DIC的最佳做法?

編輯:
我正在把它作爲Symfony2,但我認爲它可以被看作是一個更普遍的問題,適用於各種依賴注入容器。

EDIT II:
擴展的例子。

+0

Drupal 8中的一個典型例子是,如果語言發生變化,並且很多現有服務仍然使用舊語言。 – donquixote

回答

0

這可以通過一些Creational design pattern來處理:例如,工廠或構建器,您可以將所有參數傳遞給它,以確定它應該返回哪種服務類型以及如何構造服務對象圖。

Symfony2已經全部實現了。我沒有看到自己做的一點。您可以使用DPI組件代替整個symfony2堆棧。

但如果連這是太大了,你可以用Pimple去。

自己寫這可能與很多場合學習有趣的,但有時它只是重新發明輪子。

EDIT

Symfony的容器沒有的方式製成,因此它可以在生產運行時進行修改,所以它不支持$container->replace('stove', new BetterStove());。對於運行時,這又是一次。在構建時,它是不同的,一旦定義服務,可以重新定義,但我知道你問的prod運行時。所以,一旦容器完成(構建),就不會有更多的變化。

所以,在你的榜樣,你有一個Stove()Kitchen(Stove)。一段時間後,您添加BetterStove : Stove,並保留舊Stove()Kitchen(Stove)。答案是現在你有兩個廚房:一個是舊的Stove,另一個是新的BetterStove

簡單直接的解決方案是增加kitchen_with_better_stove服務,讓容器的使用者決定何時需要常規廚房,何時使用新的更好的爐子。

如果做出這個決定的邏輯,需要哪種類型的廚房,很複雜,而且你不想在解決方案中重複它,那麼你可以用工廠方法封裝它,這樣的決定,然後容器的用戶將查詢工廠,並將其稱爲getKitchen(arg1,arg2 ...)女巫將返回Kitchen與舊的常規Stove和新的BetterStove,這取決於您傳遞給工廠方法。在一個簡單的手工解決方案中,你可以讓可變的「容器」做所有類型的東西,但至少我明白DYC,這不是重點 - 容器只是組裝你的複合材料,而不是做他們的邏輯。

+0

謝謝,但我不明白這是如何回答這個問題的。當然,會有某種工廠來創建服務。但問題是,如果我想要替換它,如何將新實例傳遞給依賴它的其他服務 - 或者如何確保仍然使用舊實例的其他服務「離開遊戲」 .. – donquixote

1

在我看來,依賴注入的目的是讓你可以在創建服務定義之前決定哪個爐子最好,並且使用它的代碼完全不知道爐子的工作方式。

這使您可以將服務實現與調用代碼分離。 (理論上,因爲你仍然需要知道服務對象的界面,DIC不會幫助我們隱藏)

然後你可以模擬一個爐子放在容器中進行測試,而不會產生依賴關係生產爐可能有。

如果您在中途切換服務,則不再注入依賴關係,您只是將容器用於其他某種模式。

基本上:不要這樣做。 :-)找到導致需要不同爐子的配置,並在*.services.yml或編譯過程中處理該依賴關係,和/或致力於在不更改接口的情況下使爐子更好。

+0

剛剛發現了一些東西! Drupal 8中一個典型的例子就是login:一個提交處理程序會讓你登錄,現在用戶對象先前是空的(或者是一個空的用戶或其他),現在用一個用戶對象填充。但用戶對象實際上並不是一項服務!相反,current_user是一個AccountProxy,即使用戶更改,它仍然可以保持相同的實例。所以在上面的例子中,服務可能是一個StoveProxy。 – donquixote

+0

提交處理程序登錄,設置會話cookie,然後完成Drupal。您現在正在操作一個新的請求,因爲登錄按鈕將您鏈接到那裏。您正在運行一個全新的請求,並帶有一個全新的重新構建的容器。沒有人需要改變任何服務。 –