Guice益智游戏:批量作用封装上下文

时间:2012-12-12 19:56:24

标签: java scope guice

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

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

请注意,PolicyContext(以及我们应用中的其他*Context个对象)是一个重点关注特定域区域的值对象(代表基本的,无处不在的所需策略信息)。我很想知道你们中的模式专家是否仍然认为这是一种反模式(正如Mis {{{}}中所讨论的Misko Hevery所讨论的那样),尽管这些只是纯粹的价值对象,当然不代表“厨房”汇“。

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

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

  1. Guice丢弃旧的PolicyContext
  2. Guice使用来自数据库的PolicyContext参数构造一个新的,不可变的company/state/policyNum(无臭初始化方法)。
  3. Guice将已构造的PolicyContext注入到需要它的所有类中。
  4. 这是我的尝试性方法:

    1. 创建一个自定义范围 - 类似于http://misko.hevery.com/2008/07/18/breaking-the-law-of-demeter-is-like-looking-for-a-needle-in-the-haystack/处的Guice批量范围样本 - 其中批次的边界是外部确定的。在我们开始处理新策略的范围内,我们可以1)结束之前的“批处理”并开始新的策略。 :我无法完全按照上述网址列出的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.
      …
      
    3. 这看起来最合适吗?我知道每当我们处理新策略时,可能会有更简单的方法(例如创建一个新的PolicyContext特定注入器,这有效地创建了一个新的PolicyContext)。但是,这是架构的核心方面,所以我真的不想妥协。

      我知道,另一种选择是在这种情况下不使用DI,只使用静态PolicyContextManager类,使用单独的createget方法,前一种方法是放弃当前PolicyContext并创建/存储新的工厂的工厂,而后一种方法只返回“活动”PolicyContext)。但是我的代码最终会做手工DI,因为我会编写很多像methodThatNeedsPolicyContext(PolicyContextManager.get(), …)这样的代码。既然我们打算开始使用Guice,这种方法似乎并不是最优的。

      BTW,对于那些试图深入理解DI的人,我强烈推荐Dhanji Prasanna的“Dependency Injection”。这本专注于Guice和Spring的书绝对是不可或缺的,因为它比我遇到的任何其他内容都要深刻。

      感谢您的帮助!

1 个答案:

答案 0 :(得分:3)

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

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

你可能会发现更容易坚持你的队友从不直接注射PolicyContext而是总是注入Provider<PolicyContext>(你{{3} }})。这样可以让您在构造对象时不再考虑哪个策略处于活动状态,而是信任运行该对象方法时收到的PolicyContext。

如果一个对象没有依赖关系,你就不需要Guice来创建它 - 那就太过分了。将对象的创建移动到Guice很容易,因为它会产生很多依赖关系,因此手动构建是很痛苦的。在你不得不这样做之前不要这样做。

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

但这只是我的看法。希望它有所帮助!