2011-10-20 30 views
2

我不知道這實際上是否符合DI,因爲我不是在談論注入抽象依賴的具體實現。我只是在談論注入需要時間的東西。在我的遊戲引擎中,我想清理處理遊戲狀態(菜單,舞臺選擇,遊戲中,過場動畫等)的部分。所有這些都實現了一個通用接口。但其中的一個在遊戲中也被特別引用爲當前正在玩的等級。多級DI重構

現在有43個參考資料在我的項目中達到當前級別,通過遊戲訪問一個單身人士。例如,Game.CurrentGame.CurrentMap.Something。引用在屏幕,實體,行爲組件中,甚至在主窗體中(用於調試工具)。

我想通過注入所需的所有東西來擺脫此引用。但是CurrentMap本身並不是所需的依賴關係 - 其他的東西在它下面被訪問。所以我最初的計劃是進入每個地方,找到實際使用的東西,並通過向該類構造函數添加一個參數來注入它。這引入了另一個層次的依賴關係,所以我重複這個過程,直到一切都完成。但是問題在於它會引入更多的構造函數參數,包括不直接使用依賴項的地方。很多類會結束接受依賴關係,以便它們可以將它傳遞給它們下面的另一個對象。

什麼將是一個更清潔的替代呢?

回答

2

您正在談論一種情景,其中依賴關係必須通過幾層對象來降低它們真正需要的位置。這不需要發生。

如果一個對象本身創建了依賴關係,那麼最終會出現這個問題。如果對象通過工廠或DI容器(這只是一個奇特的工廠)給予它所需的依賴性,那麼你就不會有這個問題。所以爲了避免這個問題,你需要確定每個類是關心遊戲邏輯還是創建類。

比方說,你有對象a,它調用對象b,它調用對象c和對象c需要目前的水平和對象b沒有。

錯誤的方式做到這一點是調用new C(level);b內。正如你已經指出的,b不需要知道關卡的級別,所以看起來情況越來越糟糕,而不是越來越好。依賴注入的程度還不夠高。相反,內b創建c的,只是在b構造要求c。現在b只知道c,對level一無所知。

MISKO解釋這比我在這裏可以http://misko.hevery.com/2009/03/30/collaborator-vs-the-factory/

代碼在工廠看起來是這樣的:

Level level = new Level(); 
C c = new C(level); 
B b = new B(c); 
A a = new A(b); 

B類只知道它的直接合作者(c),並具有無依賴性Level。因爲我們在工廠中創建東西,所以沒有必要將對象圖的葉子向下傳遞到對象圖中。

如果B類負責創建c,那麼它需要知道如何創建類c的所有實例。這是錯誤的。

+0

+1我想,如果你發現自己注射的東西,所以您可以將它傳遞給別的東西構造函數,而不是使用直接的東西,那麼你應該考慮經過別的東西代替中。 –

+0

你能否澄清一下「在b'的構造函數中請求它」?這聽起來像'b'仍然知道'c',因爲它是在問它。你能給一個代碼示例嗎? – Tesserex

1

看看使用一個DI框架,如Castle Windsor,Structuremap或Unity(還有其他許多完美實用的其他框架)。

你所描述的問題不是只有這些框架解決的問題,但它是幾乎完全消除的摩擦類型的重要組成部分。

事實上,依賴關係是具體實現而不是更高級別的抽象是無關緊要的。看到這個問題(由你問真正的):

IOC/DI: Is Registering a Concrete Type a Code Smell?

0

通常我注入,保持當前狀態的跟蹤服務:

public interface IGameStateTracker 
{ 
    Game CurrentGame {get;} 
    GameMap CurrentMap {get;} 
} 

您應該只有在構造函數注入該服務直接需要它的類。恐怕我不完全理解爲什麼你會發現你需要向不直接需要的類注入東西:這可能表示你沒有使用正確的DI模式。你能提供一些代碼示例來展示這個問題的例子嗎?

我第二個Phil Sandler關於使用DI框架的觀點。它會讓生活變得更容易。但它不會彌補首先使用不正確的DI模式。