Guice Puzzler:批量作用域封装上下文 [英] Guice puzzler: Batch scoped Encapsulated Context

查看:57
本文介绍了Guice Puzzler:批量作用域封装上下文的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

We are preparing to begin using Guice in our insurance data conversion platform and I have encountered an interesting scenario that does not seem to be directly addressed in the Guice docs or any postings I have found.

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

Our platform uses the Encapsulated Context (EC) pattern in several important areas. For example, imagine we are processing a set of 10 policies. Whenever we begin processing a new policy, we wish to construct a PolicyContext object and initialize properties such as policy number, state, and company. This PolicyContext is a dependency for many classes that are involved in the conversion process.

请注意,PolicyContext(以及我们应用程序中的其他*Context对象)是一个价值对象,紧密关注特定领域(代表基本的,无所不在的策略信息).我想知道你们中间的模式大师是否仍然认为这是一种反模式(如Misko Hevery在

Note that PolicyContext (and other *Context objects within our app) is a value object that is tightly focused in a specific domain area (representing basic, ubiquitously needed policy information). I would be interested to know whether the pattern gurus among you still consider this to be an anti-pattern (as discussed by Misko Hevery in http://misko.hevery.com/2008/07/18/breaking-the-law-of-demeter-is-like-looking-for-a-needle-in-the-haystack/ ) even though these are purely value objects and certainly don’t represent the "kitchen sink."

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

Currently, we are managing PolicyContext in the worst possible way: we have a static global variable, policyContext, and policyContext.initialize(String company, String state, String policyNum) is called whenever we start processing a new policy.

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

My goal is for Guice to manage these context objects in an architecturally optimal manner so that, conceptually, whenever we begin processing a new policy:

  1. Guice丢弃了旧的PolicyContext.
  2. Guice使用来自数据库的company/state/policyNum参数构造了一个新的,不变的PolicyContext(无臭的初始化方法).
  3. Guice将已经构造的PolicyContext注入到所有需要它的类中.
  1. Guice discards the old PolicyContext.
  2. Guice construct a new, immutable PolicyContext (no smelly initialize method) using company/state/policyNum params coming from a database.
  3. Guice injects the already constructed PolicyContext into all the classes that require it.

这是我的尝试性方法:

  1. 创建一个自定义范围-与Guice批处理范围示例类似,该示例位于 http: //code.google.com/p/google-guice/wiki/CustomScopes -从外部确定批次的边界.在此范围内,我们开始处理新策略,我们可以:1)结束先前的批处理"并开始新的批处理. :有什么原因不能完全使用上述URL中列出的Guice批处理范围示例?
  2. 由于PolicyContext没有依赖关系,因此我们将AssistedInject用于 all 构造函数参数(这似乎有些奇怪).假设我们采用这种方法并生成一个PolicyContextFactory,那么在开始处理新策略时,我们将具有以下代码:

  1. Create a custom scope—something akin to the Guice batch scope sample at http://code.google.com/p/google-guice/wiki/CustomScopes--where the boundaries of the batch are externally determined. With this scope, where we begin processing a new policy, we can 1) end the previous "batch" and begin a new one. Q: Any reason I can’t use the Guice batch scope sample exactly as listed at the aforementioned URL?
  2. Since PolicyContext has no dependencies, we would use AssistedInject for all constructor parameters (which seems a bit odd). Assuming we take that approach and generate a PolicyContextFactory, it follows that where we start processing a new policy we would have code such as:

…
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的新注射器).但是,这是架构的核心方面,所以我真的不想妥协.

Does this seem optimal? I know there may be simpler approaches (e.g. creating a new, PolicyContext specific injector, whenever we process a new policy, which effectively creates a new PolicyContext). However, this is a core aspect of the architecture, so I really don’t want to compromise.

我知道,另一种选择是避免在这种情况下使用DI,而只使用带有单独的createget方法的静态PolicyContextManager类,其中前一种方法是丢弃当前方法的工厂. PolicyContext并创建/存储一个新的,而后一种方法只是返回"active"(PolicyContext).但是我的代码最终只能做手工DI,因为我将编写很多代码,例如methodThatNeedsPolicyContext(PolicyContextManager.get(), …).由于无论如何我们打算开始使用Guice,因此这种方法似乎不是最佳方法.

Another option, I know, would be to abstain from using DI in this scenario and just use a static PolicyContextManager class with separate create and get methods, where the former method is the factory that discards the current PolicyContext and creates/stores a new one, while the latter method simply returns the "active" PolicyContext). But my code will just end up doing manual DI because I’ll be writing lots of code like methodThatNeedsPolicyContext(PolicyContextManager.get(), …). Since we intend to start using Guice anyway, this approach doesn’t seem to be optimal.

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

BTW, for those attempting to cultivate a deeper understanding of DI, I highly recommend "Dependency Injection" by Dhanji Prasanna. This book, which focuses on Guice and Spring, was absolutely indispensable, as it goes so much deeper than anything else I have encountered.

感谢您的帮助!

推荐答案

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

It seems your linked SimpleScope is almost perfect for your needs exactly as it is, since your desire is to avoid passing your contexts around, and your custom scope will ensure that any @PolicyScoped bindings (presumably only your Contexts and their contents) have already been prepared ("seeded"). You'll also gain some nice multithreading capabilities, though you could get those just by turning your static reference into a static ThreadLocal.

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

You'll have to inject your policy-scoped object graph entirely between your enter and exit calls, or whatever you choose to name them. Take care to recognize that if you inject your PolicyContext in a constructor or field (saving it to the object's state), your object instance is now specific to that policy. This might seem obvious, but then again a teammate cavalierly injecting or caching dueDateCalculator may not realize that it was implicitly constructed to be the due date calculator only for policy #8675-309 and that it will provide bad answers for policy #5550-187. In particular any @Singleton object requiring a policy-scoped dependency should use a Provider, or else the singleton "remember" the policy even when you've exited your scope; this is an example of a "scope-widening injection" and Prasanna discusses it at length.

您可能会更容易坚持要求队友从不直接注入PolicyContext,而改为始终注入Provider<PolicyContext>(您

You may find it simpler to insist that your teammates never inject PolicyContext directly and instead always inject Provider<PolicyContext> (which you get for free if PolicyContext is injectable). That frees you to stop thinking about which policy was active when your objects were constructed, and instead trust the PolicyContext you receive when that object's method is run.

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

If an object has no dependencies, you don't need Guice to create it--that's just overkill. It's easy to move an object's creation to Guice once it sprouts so many dependencies that it's a pain to construct manually. Don't do it until you have to.

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

Finally, regarding Encapsulated Contexts, I happen to believe that the EC pattern is a valid refactoring as long as the context has no logic and the entire bundle of objects is applicable where the Context appears. If you can defend to me that every item in the Context is used 80% of the time you inject the context, then the code is probably shorter and easier to follow and you win. Remember that one of the benefits of dependency injection is that it is very easy to add or remove dependencies, so it becomes very easy to go from injecting one individually-bound Context property to injecting two individual Context properties to injecting the whole Context directly (and repeat for as many Contexts as you have).

不过,这只是我的看法.希望对您有帮助!

That's just my view, though. Hope it helps!

这篇关于Guice Puzzler:批量作用域封装上下文的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆