[英]Using dependency injection (via Guice) in a multi-module environment
我在涉及Guice并避免使用非Guice单身人士方面有些两难。 考虑一个包含3个模块的多模块项目: shared
, frontend
和backend
。 frontend
和backend
使用shared
模块内部的Profiling
类的实例(该实例对方法进行Profiling
,并在整个项目中广泛使用)。
几乎每个类都需要使用此Profiling
实例(包括用户连接时动态创建的User
对象)。
如果每个单独的类都需要Profiling
类的实例,则执行该方法的不同方法有一些缺点:
解决方案1(在构造函数中,复制到实例字段):
private final Profiling profiling;
@Inject
public User(Profiling profiling, String username)
缺点:您必须在每个构造函数中都包含一个Profiling
对象。 这很麻烦,而且毫无意义。 您还必须静态存储(或注入)Guice的Injector
,以便可以动态创建User
对象,而不仅仅是在首次加载程序时。
解决方案2(仅作为实例字段):
@Inject
private Profiling profiling;
public User(String username)
缺点:与上面类似,您必须使用Guice的Injector
实例化每个对象 。 这意味着要动态创建User
对象,需要在创建User
对象的类中使用Injector实例。
解决方案3(作为一个(主)类中的静态字段,由我们手动创建)
public static final Profiling PROFILING; // Can also use a get method
public Application() {
Application.PROFILING = injector.getInstance(Profiling.class)
}
缺点:与Guice的/依赖项注入建议背道而驰-创建静态(通过Application.PROFILING.start()
进行静态访问)的Singleton Profiling
对象是否违反了Guice的目的?
解决方案4(由Guice作为每个类的静态字段注入)
@Inject
private static Profiling profiling;
// You need to request every single class:
// requestStaticInjection(XXXX.class)
缺点:再次,这违反了Guice的/依赖注入建议,因为它是静态注入。 我还必须请求Guice需要将Profiler注入到的每个类(这也很麻烦)。
有没有更好的方法来设计我的项目并避免退回到我过去使用的单例设计模式?
TL; DR:我希望能够在每个单一类中访问此Profiling实例(每个模块一个),而又不退回到单例设计模式。
谢谢!
在实用上,我将使用普通的单例,可能通过单字段Enum或类似的模式。
要知道为什么,您应该问一个问题:Guice的目的是什么,并且一般来说依赖注入是什么? 目的是分离应用程序的各个部分,以便可以独立开发和测试它们,并进行集中配置和重新排列。 考虑到这一点,您需要权衡耦合 成本与去耦成本 。 这取决于您选择耦合或解耦的对象。
在这里,耦合的代价是,如果没有真实的Profiling实例(包括在测试中,包括针对User之类的模型对象),您将无法操作应用程序的任何部分。 因此,如果性能分析对其环境进行了任何假设(例如,高分辨率系统定时调用的可用性),您将无法使用诸如User之类的类,而不能禁用性能分析。 此外,如果为了测试隔离而希望使用新的(非Singleton)Profiling实例对测试进行概要分析,则需要单独实施该实例。 但是,如果您的Profiling类非常轻巧,不会给您带来沉重的负担,那么您仍然可以选择这种方式。
去耦的代价是,它可以迫使每个对象成为可注射对象,而不是可更新对象 。 这样,您便可以在类和测试中替代新的/虚拟/伪造的Profiling实现,并重新配置为在不同的容器中使用不同的Profiling行为,尽管如果您没有理由替换的话,灵活性可能不会立即带来好处。这些实现。 对于诸如稍后创建的User之类的类,您将需要工厂实现,例如通过Guice辅助注入或AutoFactory代码生成提供的那些。 (请记住,您可以通过注入创建对象任意数量的Provider<T>
而不是T
你否则将注入的任何对象,并注入工厂实例会像定制提供商采取get
你选择的参数。)
关于您的解决方案:
解决方案1和2,按对象注入:这是工厂发光的地方。 (我会选择使用构造函数注入,因此我会在它们之间使用解决方案1。)当然,创建新User的所有内容都需要注入User.Factory
,这样可能会变得User.Factory
范围内的项目转换为将代码库中的每个类转换为DI的项目,这对于您现在想做的事情来说可能是不可接受的成本。
// Nested interface for your Factory: public interface Factory { User get(String username); } // Mark fields that the user provides: @Inject public User(Profiling profiling, @Assisted String username) { ... } // Wire up your Factory in a Module's configure: install(new FactoryModuleBuilder().implement(User.Factory.class)); // Now you can create new Users on the fly: @Inject User.Factory userFactory; User myNewUser = userFactory.get("timothy");
解决方案3,请求静态注入一个主持有人,这与我的想法差不多:对于不是通过依赖项注入创建的对象,请为单个类(如ProfilingHolder
或类似的东西)请求静态注入。 出于灵活性的考虑,您甚至可以设置无操作行为:
public class ProfilingHolder { // Populate with requestStaticInjection(ProfilingHolder.class). @Inject static Profiling profilingInstance; private ProfilingHolder() { /* static access only */ } public static Profiling getInstance() { if (profilingInstance == null) { // Run without profiling in isolation and tests. return new NoOpProfilingInstance(); } return profilingInstance; } }
当然,如果您依赖于对VM单例的调用,那么您实际上是在拥抱普通的VM全局静态单例模式,只要有相互作用就可以使用Guice。 您可以轻松地扭转这种模式,并具有Guice模块bind(Profiling.class).toInstance(Profiling.INSTANCE);
并获得相同的效果(假设无需Guice即可实例化分析)。
解决方案4,每个类的requestStaticInjection是我不考虑的唯一类 。 类列表太长,而且可能他们会改变Profiling
是太渺茫。 您会将模块变成维护成本高的购物清单,而不是任何有价值的配置,并且迫使自己破坏封装或使用Guice进行测试。
因此,总而言之,我会为您当前的可注入对象选择Guice单例注入,为您当前的可更新对象选择普通单例,并且如果/当您有任何新可再生对象实现向可 注入对象的跃迁时 ,可以选择迁移到工厂。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.