我们正准备在我们的保险数据转换平台中开始使用Guice,我遇到了一个有趣的场景,似乎没有在Guice文档或我发现的任何帖子中直接解决。
我们的平台在几个重要领域使用封装上下文(EC)模式。例如,假设我们正在处理一组10个策略。每当我们开始处理新策略时,我们希望构造一个PolicyContext
对象并初始化属性,例如策略号,状态和公司。此PolicyContext
是转换过程中涉及的许多类的依赖项。
请注意,PolicyContext
(以及我们应用中的其他*Context
个对象)是一个重点关注特定域区域的值对象(代表基本的,无处不在的所需策略信息)。我很想知道你们中的模式专家是否仍然认为这是一种反模式(正如Mis {{{}}中所讨论的Misko Hevery所讨论的那样),尽管这些只是纯粹的价值对象,当然不代表“厨房”汇“。
目前,我们以最糟糕的方式管理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
特定注入器,这有效地创建了一个新的PolicyContext
)。但是,这是架构的核心方面,所以我真的不想妥协。
我知道,另一种选择是在这种情况下不使用DI,只使用静态PolicyContextManager
类,使用单独的create
和get
方法,前一种方法是放弃当前PolicyContext
并创建/存储新的工厂的工厂,而后一种方法只返回“活动”PolicyContext
)。但是我的代码最终会做手工DI,因为我会编写很多像methodThatNeedsPolicyContext(PolicyContextManager.get(), …)
这样的代码。既然我们打算开始使用Guice,这种方法似乎并不是最优的。
感谢您的帮助!
答案 0 :(得分:3)
似乎您的链接SimpleScope
几乎完全符合您的需求,因为您希望避免传递您的上下文,并且您的自定义范围将确保任何@PolicyScoped
绑定(可能是只有你的上下文及其内容)已经准备好了(“播种”)。您还将获得一些不错的多线程功能,但您可以通过将静态引用转换为静态ThreadLocal来获得这些功能。
您必须完全在enter
和exit
来电之间注入您的政策范围对象图,或者您选择为其命名的任何内容。请注意,如果将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(和重复尽可能多的上下文。
但这只是我的看法。希望它有所帮助!