3

我们正准备开始在我们的保险数据转换平台中使用 Guice,我遇到了一个有趣的场景,在 Guice 文档或我发现的任何帖子中似乎都没有直接解决。

我们的平台在几个重要领域使用封装上下文 (EC) 模式。例如,假设我们正在处理一组 10 个策略。每当我们开始处理新策略时,我们希望构造一个PolicyContext对象并初始化诸如策略编号、状态和公司之类的属性。这PolicyContext是转换过程中涉及的许多类的依赖项。

请注意,PolicyContext(以及*Context我们应用程序中的其他对象)是一个紧密关注特定域区域的值对象(代表基本的、普遍需要的策略信息)。我很想知道你们中的模式专家是否仍然认为这是一种反模式(正如 Misko Hevery 在http://misko.hevery.com/2008/07/18/break-the-law-中所讨论的那样of-demeter-is-like-looking-for-a-needle-in-the-haystack/ ) 尽管这些是纯粹的价值对象,当然不代表“厨房水槽”。</p>

目前,我们正在PolicyContext以最糟糕的方式进行管理:我们有一个静态全局变量 , policyContextpolicyContext.initialize(String company, String state, String policyNum)每当我们开始处理新策略时都会调用它。

我的目标是让 Guice 以架构上优化的方式管理这些上下文对象,以便从概念上讲,每当我们开始处理新策略时:

  1. 古斯弃旧PolicyContext
  2. Guice使用来自数据库的参数构造了一个新的、不可变的PolicyContext(没有臭名昭著的初始化方法) 。company/state/policyNum
  3. Guice 将已经构建PolicyContext的注入到所有需要它的类中。

这是我的暂定方法:

  1. 创建一个自定义范围——类似于http://code.google.com/p/google-guice/wiki/CustomScopes上的 Guice 批处理范围示例——其中批处理的边界由外部确定。有了这个范围,我们开始处理一个新的策略,我们可以 1) 结束前一个“批次”并开始一个新的。:为什么我不能完全按照上述 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,绝对是必不可少的,因为它比我遇到的任何其他东西都要深入得多。

谢谢你的帮助!

4

1 回答 1

3

看起来您的链接SimpleScope几乎完全符合您的需求,因为您希望避免传递您的上下文,并且您的自定义范围将确保任何@PolicyScoped绑定(大概只有您的上下文及其内容)已经准备好(“播种”)。您还将获得一些不错的多线程功能,尽管您可以通过将静态引用转换为静态 ThreadLocal 来获得这些功能。

您必须在enterexit调用之间完全注入策略范围的对象图,或者您选择的任何名称。请注意,如果您将 PolicyContext 注入构造函数或字段(将其保存到对象的状态),则您的对象实例现在特定于该策略。这可能看起来很明显,但是一个随意注入或缓存的队友dueDateCalculator可能没有意识到它被隐式构造为仅适用于策略 #8675-309 的到期日期计算器,并且它将为策略 #5550-187 提供错误的答案。特别是任何@Singleton需要策略范围依赖的对象都应该使用提供者,否则即使您退出范围,单例也会“记住”策略;这是“范围扩大注入”的示例,并且Prasanna 详细讨论了它

您可能会发现坚持让您的队友从不直接注射PolicyContext而是始终注射Provider<PolicyContext>(如果是可注射的,您可以免费获得PolicyContext)更简单。这使您不必再考虑在构造对象时哪个策略处于活动状态,而是信任您在该对象的方法运行时收到的 PolicyContext。

如果一个对象没有依赖关系,则不需要 Guice 来创建它——这只是矫枉过正。一旦对象产生了如此多的依赖项以至于手动构建很痛苦,就很容易将对象的创建移动到 Guice。除非你必须这样做,否则不要这样做。

最后,关于封装上下文,我碰巧相信 EC 模式是一种有效的重构,只要上下文没有逻辑并且整个对象包适用于上下文出现的地方。如果您可以向我辩护说 Context 中的每个项目在您注入上下文的时间中有 80% 被使用,那么代码可能更短且更易于遵循并且您赢了。请记住,依赖注入的好处之一是添加或删除依赖项非常容易,因此从注入一个单独绑定的 Context 属性到注入两个单独的 Context 属性再到直接注入整个 Context 变得非常容易(并且重复尽可能多的上下文)。

不过,这只是我的看法。希望能帮助到你!

于 2012-12-13T04:56:04.267 回答