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

我们的平台在几个重要领域使用了封装上下文(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)

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

  • Guice丢弃旧的PolicyContext
  • Guice使用来自数据库的PolicyContext参数构造了一个新的,不变的company/state/policyNum(无臭的初始化方法)。
  • Guice将已经构造的PolicyContext注入所有需要它的类中。

  • 这是我的尝试方法:
  • 创建一个自定义范围-类似于http://code.google.com/p/google-guice/wiki/CustomScopes上的Guice批处理范围示例-该批处理的边界在外部确定。在此范围内,我们开始处理新策略,我们可以1)结束先前的“批处理”并开始新的批处理。 :有什么原因不能完全使用上述URL中列出的Guice批处理范围示例?
  • 由于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,而只使用带有单独的PolicyContextManagercreate方法的静态get类,其中前一种方法是丢弃当前PolicyContext并创建/存储新PolicyContext的工厂,而后一种方法只是返回“active” methodThatNeedsPolicyContext(PolicyContextManager.get(), …))。但是我的代码最终只能做手工DI,因为我会写很多代码,例如ojit_code。由于我们打算开始使用Guice,因此这种方法似乎不是最佳方法。

    顺便说一句,对于那些试图加深对DI理解的人,我强烈推荐Dhanji Prasanna的“依赖注入”。这本书侧重于Guice和Spring,是绝对必不可少的,因为它比我所遇到的其他任何东西都深入。

    谢谢你的帮助!

    最佳答案

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

    您必须将整个策略范围内的对象图完全插入enterexit调用之间,或者您选择命名的对象之间。请注意,如果将PolicyContext注入构造函数或字段(将其保存到对象的状态),则对象实例现在特定于该策略。这看起来似乎很明显,但是再次用队友大胆地注入或缓存dueDateCalculator可能不会意识到它隐式构造为仅用于策略#8675-309的到期日计算器,并且它将为策略#5550-187提供错误的答案。特别是,任何需要策略作用域依赖性的@Singleton对象都应使用Provider,否则即使退出范围,单例也“记住”策略;这是“范围扩大注入”和Prasanna discusses it at length的示例。

    您可能会发现,坚持团队成员不要直接注入PolicyContext而是始终注入Provider<PolicyContext>(即get for free if PolicyContext is injectable)更为简单。这使您无需再考虑构造对象时哪个策略处于 Activity 状态,而可以信任运行该对象的方法时收到的PolicyContext。

    如果一个对象没有依赖性,则不需要Guice来创建它-这太过分了。一旦对象的创建萌生了太多的依赖关系,就很容易将其创建的内容移动到Guice,以至于手动构建很麻烦。除非必须这样做,否则不要这样做。

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

    不过,那只是我的观点。希望能帮助到你!

    10-08 12:43