[英]Using dependency injection (via Guice) in a multi-module environment
我在涉及Guice並避免使用非Guice單身人士方面有些兩難。 考慮一個包含3個模塊的多模塊項目: shared
, frontend
和backend
。 frontend
和backend
使用shared
模塊內部的Profiling
類的實例(該實例對方法進行Profiling
,並在整個項目中廣泛使用)。
幾乎每個類都需要使用此Profiling
實例(包括用戶連接時動態創建的User
對象)。
如果每個單獨的類都需要Profiling
類的實例,則執行該方法的不同方法有一些缺點:
解決方案1(在構造函數中,復制到實例字段):
private final Profiling profiling;
@Inject
public User(Profiling profiling, String username)
缺點:您必須在每個構造函數中都包含一個Profiling
對象。 這很麻煩,而且毫無意義。 您還必須靜態存儲(或注入)Guice的Injector
,以便可以動態創建User
對象,而不僅僅是在首次加載程序時。
解決方案2(僅作為實例字段):
@Inject
private Profiling profiling;
public User(String username)
缺點:與上面類似,您必須使用Guice的Injector
實例化每個對象 。 這意味着要動態創建User
對象,需要在創建User
對象的類中使用Injector實例。
解決方案3(作為一個(主)類中的靜態字段,由我們手動創建)
public static final Profiling PROFILING; // Can also use a get method
public Application() {
Application.PROFILING = injector.getInstance(Profiling.class)
}
缺點:與Guice的/依賴項注入建議背道而馳-創建靜態(通過Application.PROFILING.start()
進行靜態訪問)的Singleton Profiling
對象是否違反了Guice的目的?
解決方案4(由Guice作為每個類的靜態字段注入)
@Inject
private static Profiling profiling;
// You need to request every single class:
// requestStaticInjection(XXXX.class)
缺點:再次,這違反了Guice的/依賴注入建議,因為它是靜態注入。 我還必須請求Guice需要將Profiler注入到的每個類(這也很麻煩)。
有沒有更好的方法來設計我的項目並避免退回到我過去使用的單例設計模式?
TL; DR:我希望能夠在每個單一類中訪問此Profiling實例(每個模塊一個),而又不退回到單例設計模式。
謝謝!
在實用上,我將使用普通的單例,可能通過單字段Enum或類似的模式。
要知道為什么,您應該問一個問題:Guice的目的是什么,並且一般來說依賴注入是什么? 目的是分離應用程序的各個部分,以便可以獨立開發和測試它們,並進行集中配置和重新排列。 考慮到這一點,您需要權衡耦合 成本與去耦成本 。 這取決於您選擇耦合或解耦的對象。
在這里,耦合的代價是,如果沒有真實的Profiling實例(包括在測試中,包括針對User之類的模型對象),您將無法操作應用程序的任何部分。 因此,如果性能分析對其環境進行了任何假設(例如,高分辨率系統定時調用的可用性),您將無法使用諸如User之類的類,而不能禁用性能分析。 此外,如果為了測試隔離而希望使用新的(非Singleton)Profiling實例對測試進行概要分析,則需要單獨實施該實例。 但是,如果您的Profiling類非常輕巧,不會給您帶來沉重的負擔,那么您仍然可以選擇這種方式。
去耦的代價是,它可以迫使每個對象成為可注射對象,而不是可更新對象 。 這樣,您便可以在類和測試中替代新的/虛擬/偽造的Profiling實現,並重新配置為在不同的容器中使用不同的Profiling行為,盡管如果您沒有理由替換的話,靈活性可能不會立即帶來好處。這些實現。 對於諸如稍后創建的User之類的類,您將需要工廠實現,例如通過Guice輔助注入或AutoFactory代碼生成提供的那些。 (請記住,您可以通過注入創建對象任意數量的Provider<T>
而不是T
你否則將注入的任何對象,並注入工廠實例會像定制提供商采取get
你選擇的參數。)
關於您的解決方案:
解決方案1和2,按對象注入:這是工廠發光的地方。 (我會選擇使用構造函數注入,因此我會在它們之間使用解決方案1。)當然,創建新User的所有內容都需要注入User.Factory
,這樣可能會變得User.Factory
范圍內的項目轉換為將代碼庫中的每個類轉換為DI的項目,這對於您現在想做的事情來說可能是不可接受的成本。
// Nested interface for your Factory: public interface Factory { User get(String username); } // Mark fields that the user provides: @Inject public User(Profiling profiling, @Assisted String username) { ... } // Wire up your Factory in a Module's configure: install(new FactoryModuleBuilder().implement(User.Factory.class)); // Now you can create new Users on the fly: @Inject User.Factory userFactory; User myNewUser = userFactory.get("timothy");
解決方案3,請求靜態注入一個主持有人,這與我的想法差不多:對於不是通過依賴項注入創建的對象,請為單個類(如ProfilingHolder
或類似的東西)請求靜態注入。 出於靈活性的考慮,您甚至可以設置無操作行為:
public class ProfilingHolder { // Populate with requestStaticInjection(ProfilingHolder.class). @Inject static Profiling profilingInstance; private ProfilingHolder() { /* static access only */ } public static Profiling getInstance() { if (profilingInstance == null) { // Run without profiling in isolation and tests. return new NoOpProfilingInstance(); } return profilingInstance; } }
當然,如果您依賴於對VM單例的調用,那么您實際上是在擁抱普通的VM全局靜態單例模式,只要有相互作用就可以使用Guice。 您可以輕松地扭轉這種模式,並具有Guice模塊bind(Profiling.class).toInstance(Profiling.INSTANCE);
並獲得相同的效果(假設無需Guice即可實例化分析)。
解決方案4,每個類的requestStaticInjection是我不考慮的唯一類 。 類列表太長,而且可能他們會改變Profiling
是太渺茫。 您會將模塊變成維護成本高的購物清單,而不是任何有價值的配置,並且迫使自己破壞封裝或使用Guice進行測試。
因此,總而言之,我會為您當前的可注入對象選擇Guice單例注入,為您當前的可更新對象選擇普通單例,並且如果/當您有任何新可再生對象實現向可 注入對象的躍遷時 ,可以選擇遷移到工廠。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.