2012-12-12 15 views
3

我們正準備在我們的保險數據轉換平臺中開始使用Guice,並且我遇到了一個有趣的場景,似乎並沒有在Guice文檔或我發現的任何帖子中直接解決。Guice puzzler:批量作用域封裝上下文

我們的平臺在幾個重要領域使用封裝上下文(EC)模式。例如,假設我們正在處理一組10個策略。無論何時我們開始處理新政策,我們都希望構建一個PolicyContext對象並初始化諸如保單號,州和公司等屬性。此PolicyContext是轉換過程中涉及的許多類的依賴項。

請注意PolicyContext(和我們應用程序中的其他*Context對象)是一個值集合在特定領域(代表基本的,無所不在的策略信息)的對象。我很想知道你們當中的模特大師是否仍然認爲這是一種反模式(正如Misko Hevery在http://misko.hevery.com/2008/07/18/breaking-the-law-of-demeter-is-like-looking-for-a-needle-in-the-haystack/中所討論的那樣),儘管這些都是純粹的價值對象,當然不代表「廚房水槽」。

目前,我們正在以最糟糕的方式管理PolicyContext:我們有一個靜態全局變量policyContext,並且在我們開始處理新策略時調用policyContext.initialize(String company, String state, String policyNum)

我的目標是要吉斯在架構優化的方式管理這些上下文對象,這樣,在概念上,每當我們開始處理新的政策:

  1. 吉斯丟棄舊PolicyContext
  2. Guice使用來自數據庫的company/state/policyNum params構造一個新的,不可變的PolicyContext(無臭初始化方法)。
  3. Guice將已構造的PolicyContext注入到所有需要它的類中。

這裏是我試探性的方法:

  1. 創建一個自定義的範圍,一些類似於在http://code.google.com/p/google-guice/wiki/CustomScopes的吉斯批次樣品範圍的--where批次的邊界外部確定。在這個範圍內,在我們開始處理新政策的地方,我們可以1)結束以前的「批次」並開始一個新的政策。 Q:我無法完全按照上述URL中列出的方式使用Guice批量範圍示例?
  2. 既然PolicyContext沒有依賴關係,我們將使用AssistedInject爲所有構造函數參數(這似乎有點奇怪)。假設我們採取這種做法,並生成一個PolicyContextFactory,接下去,我們開始處理新的政策,我們將有這樣的代碼:

    … 
    scope.exit(); 
    scope.enter(); 
    @Inject private PolicyContextFactory policyContextFactory; 
    policyContextFactory.create(company, state, policyNum); // the parameters come from a database record. 
    // Note that we don’t need to actually store the created instance; it will be injected elsewhere into various class constructors. 
    … 
    

這個問題似乎最佳?我知道可能有更簡單的方法(例如,每當我們處理新策略時創建新的PolicyContext特定注入器,這會有效地創建新的PolicyContext)。然而,這是架構的核心部分,所以我真的不想妥協。

另一種選擇,我知道,是在這種情況下使用DI棄權,只需使用一個靜態PolicyContextManager類單獨createget方法,其中,前一種方法是,丟棄當前PolicyContext並創建工廠/賣場一個新的,而後一種方法只是返回「活動」PolicyContext)。但是我的代碼最終會做手動DI,因爲我會寫很多代碼,如methodThatNeedsPolicyContext(PolicyContextManager.get(), …)。既然我們打算開始使用Guice,這種方法似乎並不是最佳的。

順便說一句,對於那些試圖培養DI更深入的瞭解,我深受Dhanji人員Prasanna推薦「依賴注入」。這本關注Guice和Spring的書絕對是不可或缺的,因爲它比我遇到的任何其他東西都要深刻得多。

感謝您的幫助!

回答

3

看來你的鏈接SimpleScope幾乎是完美的滿足您的需求,正是因爲它是,因爲你的願望是避免周圍路過你的背景,你的自定義的範圍將確保任何@PolicyScoped綁定(大概只有你的上下文及其內容)已經準備好(「種子」)。您還可以獲得一些很好的多線程功能,但只需將您的靜態引用轉換爲靜態ThreadLocal即可。

您必須在您的enterexit調用之間完全注入您的策略範圍對象圖,或者選擇任何您想要命名的策略範圍對象圖。請注意,如果將PolicyContext注入構造函數或字段(將其保存爲對象的狀態),那麼您的對象實例現在特定於該策略。這可能看起來很明顯,但是再次,一個傲慢注入或緩存的隊友可能沒有意識到,它被隱含地構造爲僅適用於策略#8675-309的截止日期計算器,並且它將爲策略#5550-187提供不好的答案。特別是任何@Singleton對象需要政策範圍依賴應該使用一個供應商,否則單「記住」的政策,即使你退出你的範圍;這是「範圍擴大注入」和Prasanna discusses it at length的示例。

您可能會發現它更簡單的堅持,你的隊友從未直接注入PolicyContext,而是總是注入Provider<PolicyContext>(你get for free if PolicyContext is injectable)。這使您可以在構建對象時停止考慮哪些策略處於活動狀態,而是相信您運行對象方法時收到的PolicyContext。

如果一個對象沒有依賴性,你不需要吉斯創建它 - 這只是矯枉過正。將一個對象的創建移動到Guice中很容易,一旦它產生了這麼多的依賴關係,手動構建就很麻煩。不要做,直到你必須做。

最後,關於封裝上下文,我碰巧認爲EC模式是有效的重構只要上下文沒有邏輯,整個對象包在上下文出現的地方都適用。如果你可以向我辯護,在注入上下文的80%的時間內,上下文中的每個項目都被使用,那麼代碼可能更短,更容易遵循,並且你贏了。請記住,依賴注入的好處之一是添加或刪除依賴非常容易,所以從注入一個單獨綁定的Context屬性到注入兩個單獨的Context屬性直接注入整個Context(以及重複儘可能多的上下文)。

這只是我的看法,雖然。希望能幫助到你!

+0

非常感謝傑夫。清楚地陳述並解決一些重要的細微差別。我確實有幾個相關的問題,但我會分開發布。 –

+0

+1用於注入提供者並創建自定義範圍。不要以爲你需要綁定才能注入提供商BTW。 – RobbieV

+0

@RobbieV正如我提到的「免費獲得」評論,如果Guice可以注入'Foo',它可以注入'Provider '而不需要額外的代碼。如果它是一個具體類,'Foo'可以被隱含地綁定,不需要綁定'Foo'或'Provider ',但是如果'Foo'是一個接口,你仍然需要將它綁定到某個地方。儘管 - 我已經說過「綁定'Provider '」,我的意思是「注入'Provider 」 - 現在已經修復了。 –