我们正准备开始在我们的保险数据转换平台中使用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以架构上最佳的方式管理这些上下文对象,以便从概念上讲,每当我们开始处理新策略时:
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
的工厂,而后一种方法只是返回“active” methodThatNeedsPolicyContext(PolicyContextManager.get(), …)
)。但是我的代码最终只能做手工DI,因为我会写很多代码,例如ojit_code。由于我们打算开始使用Guice,因此这种方法似乎不是最佳方法。顺便说一句,对于那些试图加深对DI理解的人,我强烈推荐Dhanji Prasanna的“依赖注入”。这本书侧重于Guice和Spring,是绝对必不可少的,因为它比我所遇到的其他任何东西都深入。
谢谢你的帮助!
最佳答案
似乎已链接的 SimpleScope
几乎完全适合您的需求,因为您希望避免传递上下文,并且您的自定义范围将确保已准备好所有@PolicyScoped
绑定(大概只有您的上下文及其内容) (“播种”)。您还可以获得一些不错的多线程功能,尽管您可以通过将静态引用转换为静态ThreadLocal来获得这些功能。
您必须将整个策略范围内的对象图完全插入enter
和exit
调用之间,或者您选择命名的对象之间。请注意,如果将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(以及重复尽可能多的上下文)。
不过,那只是我的观点。希望能帮助到你!