我们正准备开始在我们的保险数据转换平台中使用 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/ 中所讨论的那样),即使这些是纯粹的值(value)对象并且当然不代表“厨房水槽”。 ”
目前,我们正在管理 PolicyContext
以最糟糕的方式:我们有一个静态全局变量 policyContext
, 和 policyContext.initialize(String company, String state, String policyNum)
每当我们开始处理新策略时都会调用它。
我的目标是让 Guice 以架构上优化的方式管理这些上下文对象,以便从概念上讲,每当我们开始处理新策略时:
PolicyContext
. PolicyContext
(无臭初始化方法)使用company/state/policyNum
来自数据库的参数。 PolicyContext
进入所有需要它的类。 这是我的暂定方法:
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
特定注入(inject)器,这实际上创建了一个新的 PolicyContext
)。然而,这是架构的核心方面,所以我真的不想妥协。我知道,另一种选择是在这种情况下避免使用 DI,而只使用静态
PolicyContextManager
带有单独 create
的类和 get
方法,其中前一种方法是丢弃当前 PolicyContext
的工厂并创建/存储一个新的,而后一种方法只返回“Activity ”PolicyContext
)。但是我的代码最终会执行手动 DI,因为我将编写大量代码,例如 methodThatNeedsPolicyContext(PolicyContextManager.get(), …)
.由于我们无论如何都打算开始使用 Guice,所以这种方法似乎不是最佳的。顺便说一句,对于那些试图深入了解 DI 的人,我强烈推荐 Dhanji Prasanna 的“依赖注入(inject)”。这本书专注于 Guice 和 Spring,绝对是必不可少的,因为它比我遇到的任何其他东西都要深入得多。
谢谢你的帮助!
最佳答案
看来您的链接 SimpleScope
几乎完全符合您的需求,因为您希望避免传递您的上下文,并且您的自定义范围将确保任何 @PolicyScoped
绑定(bind)(大概只有您的上下文及其内容)已经准备好(“种子”)。您还将获得一些不错的多线程功能,尽管您可以通过将静态引用转换为静态 ThreadLocal 来获得这些功能。
您必须在 enter
之间完全注入(inject)策略范围的对象图。和 exit
电话,或任何你选择的名字。请注意,如果您将 PolicyContext 注入(inject)构造函数或字段(将其保存到对象的状态),那么您的对象实例现在特定于该策略。这似乎很明显,但又是一个队友漫不经心地注入(inject)或缓存 dueDateCalculator
可能没有意识到它被隐式构造为仅适用于政策 #8675-309 的到期日期计算器,并且它将为政策 #5550-187 提供错误的答案。特别是任何@Singleton
需要策略范围依赖的对象应该使用提供者,否则即使您退出范围,单例也会“记住”策略;这是一个“范围扩大注入(inject)”的例子,Prasanna discusses it at length .
你可能会发现坚持让你的队友永远不要注入(inject) PolicyContext
会更简单。直接而不是总是注入(inject) Provider<PolicyContext>
(您 get for free if PolicyContext
is injectable )。这使您不必再考虑在构造对象时哪个策略处于 Activity 状态,而是信任您在该对象的方法运行时收到的 PolicyContext。
如果一个对象没有依赖关系,则不需要 Guice 来创建它——这只是矫枉过正。一旦对象产生了如此多的依赖项以至于手动构建很痛苦,就很容易将对象的创建移动到 Guice。除非你必须这样做,否则不要这样做。
最后,关于封装上下文,我碰巧相信 EC 模式是一种有效的重构,只要上下文没有逻辑并且整个对象包适用于上下文出现的地方。如果您可以向我辩护说 Context 中的每个项目在您注入(inject)上下文的时间中有 80% 被使用,那么代码可能更短且更易于遵循并且您赢了。请记住,依赖注入(inject)的好处之一是添加或删除依赖项非常容易,因此从注入(inject)一个单独绑定(bind)的 Context 属性到注入(inject)两个单独的 Context 属性再到直接注入(inject)整个 Context 变得非常容易(并且重复尽可能多的上下文)。
不过,这只是我的看法。希望能帮助到你!
关于java - Guice 谜题 : Batch scoped Encapsulated Context,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13847558/