java - 分层设计中对象创建中的@Assisted\@Provider用法

标签 java dependency-injection guice assisted-inject

这个问题是关于 Guice @Assisted 和 @Provides 的正确用法以及如何使用。

我目前引用的设计是这样的: 层次结构顶部的类也是唯一暴露给客户端的类(基本上是公共(public) API),它看起来像这样:

public class Manager{
public Manager(int managerId, ShiftFactory sf, WorkerFactory wf);
 // methods ...
}

正如您可能了解的那样,id 是由用户在创建时提供的(@Assisted?) 但其他的不是,它们只是工厂。

Manager创建类Shift的实例。 类 Shift 创建类 Worker 的实例。

现在,为了创建类Shift,我们使用它的构造函数:

public Shift(int managerId, int shiftId, WorkerFactory wf);

shiftIdManager 提供,其余的与 Manager 构造函数中的对象相同。

为了创建Worker,我们使用2个静态工厂方法(但它可以更改..):

  public Worker createWorkerTypeA(int shiftId, int workerId)
  public Worker createWorkerTypeB(int shiftId, int workerId)

workerIdShift 类提供。其余部分由 Shift 构造函数委托(delegate)。

正确的、Guice-y 的方法是什么? 我应该把@Assisted放在哪里? @提供?

我真的想要一个代码示例,包括抽象模块,因为到目前为止我所看到的代码示例对我来说还无法理解。

谢谢

最佳答案

在较高的层面上,您希望工厂隐藏可预测的依赖项,这样您只需指定发生更改的依赖项。拥有工厂实例的人只需要传入数据,而不是工厂或依赖项。我想象的界面是这样的。

interface ManagerFactory {
  Manager createManager(int managerId);
}
interface ShiftFactory {
  Shift createShift(int managerId, int shiftId);
}
interface WorkerFactory { // The two methods here might be difficult to automate.
  Worker createWorkerA(int managerId, int shiftId, int workerId);
  Worker createWorkerB(int managerId, int shiftId, int workerId);
}

class Manager {
  @Inject ShiftFactory shiftFactory;  // set by Guice, possibly in constructor
  private final int managerId;        // set in constructor

  Shift createShift(int shiftId) {
    shiftFactory.createWorkerA(this.managerId, shiftId);  // or B?
  }
}

class Shift {
  @Inject WorkerFactory workerFactory;  // set by Guice, possibly in constructor
  private final int managerId;          // set in constructor
  private final int shiftId;            // set in constructor

  Worker createWorker(int workerId) {
    shiftFactory.createShift(this.managerId, this.shiftId, workerId);
  }
}

请注意,Manager 根本不关心 worker - 它不会创建 worker ,因此与您的问题不同,您不必接受 WorkerFactory 只是将其传递给您的类次。这就是依赖注入(inject)的吸引力的一部分;您不必担心中间管理器(middle-Manager?)及其依赖项的依赖项。

另请注意,对于构造函数之外的公共(public) API,任何 Factory 接口(interface)或实现都看不到。这些是实现细节,您可以沿着对象层次结构进行操作,而无需从外部调用对象层次结构。

现在,ManagerFactory 实现会是什么样子?也许是这样的:

class ManualManagerFactory {
  // ShiftFactory is stateless, so you don't have to inject a Provider,
  // but if it were stateful like a Database or Cache this would matter more.
  @Inject Provider<ShiftFactory> shiftFactoryProvider;

  @Override public Manager createManager(int managerId) {
    return new Manager(managerId, shiftFactoryProvider.get());
  }
}

...但这很大程度上是样板文件,当有大量注入(inject)或非注入(inject)参数时可能更是如此。 Guice 可以为您做到这一点,只要您仍然提供 ManagerFactory 接口(interface)并注释构造函数即可:

class Manager {
  private final ShiftFactory shiftFactory;  // set in constructor
  private final int managerId;              // set in constructor

  @Inject Manager(ShiftFactory shiftFactory, @Assisted int managerId) {
    this.shiftFactory = shiftFactory;
    this.managerId = managerId;
  }

  // ...
}

// and in your AbstractModule's configure method:
new FactoryModuleBuilder().build(ManagerFactory.class);

就是这样。 Guice 通过读取 Manager 方法的返回类型,将其与 @Inject 和 @Assisted 注释以及接口(interface)方法参数进行匹配,并从那里计算出来,来创建自己的基于反射的 ManagerFactory 实现。您甚至不需要调用 FactoryModuleBuilder 上的 implement 方法,除非 Manager 是一个接口(interface);那么你必须告诉 Guice 要创建哪种具体类型。

<小时/>

对于踢腿和微笑,让我们看看Google's code-generating AutoFactory package同样的事情。 :

@AutoFactory(
    className = "AutoManagerFactory", implementing = {ManagerFactory.class})
class Manager {
  private final ShiftFactory shiftFactory;  // set in constructor
  private final int managerId;              // set in constructor

  @Inject Manager(@Provided ShiftFactory shiftFactory, int managerId) {
    this.shiftFactory = shiftFactory;
    this.managerId = managerId;
  }

  // ...
}

几乎相同,对吧?这将生成一个 Java 类(带有您可以阅读的源代码!),该类检查 Manager 类及其构造函数,读取 @Provided 注释(注意,@Provided 与 FactoryModuleBuilder 的 @Assisted 相反),并委托(delegate)给构造函数及其组合参数和注入(inject)字段。 Auto 与 Guice 以及 Dagger 和其他 JSR-330 依赖注入(inject)框架一起使用还有另外两个优点:

  1. 这是正常的 Java 代码,没有 Guice 及其 FactoryModuleBuilder 中的反射; Android 上的反射性能很差,因此这可以带来不错的性能提升。

  2. 通过代码生成,您甚至不需要创建 ManagerFactory 接口(interface) - 如果没有 @AutoFactory 的任何参数,您最终会得到一个 final class ManagerFactory { ... }这正是 Guice 通过 FactoryModuleBuilder 连接的行为。当然,您可以自己自定义名称和接口(interface),这也可能对您的开发人员有所帮助,因为生成的代码有时在工具和 IDE 中看起来不太好。

<小时/>

更新回答评论:

  • 关于createWorker:是的,抱歉,复制粘贴错误。
  • 关于自动化:这是因为 Assisted Inject 和 AutoFactory 都没有很好的方法来委托(delegate)静态方法,或者使用具有相同辅助(用户提供的)参数的构造函数。在这种情况下,您可能必须编写自己的工厂。
  • 关于 Manager 不需要 WorkerFactory:Manager 需要 WorkerFactory 的唯一原因是它通过调用构造函数创建 ShiftFactory 或 Shift 本身。请注意,我的示例没有执行这些操作:您让依赖项注入(inject)框架 (Guice) 提供依赖项,这意味着 WorkerFactory 隐藏在 Guice 已提供的 ShiftFactory 中。

关于java - 分层设计中对象创建中的@Assisted\@Provider用法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40409840/

相关文章:

java - 将 key 动态传递给@Named 注解

java - Spring Security 3.1.3 问题

java - 使用 Spring 将 boolean bean 创建转换为 boolean 值

c# - 如何使用 Unity IoC 在解析的具体类型上指定构造函数值?

dependency-injection - 具有多个端点和依赖项注入(inject)的 Service Fabric

java - guice 中的注入(inject)并不适用于任何地方

java - 切换 JTextField 可见性

java - Java中使用构造函数的this()调用同一个类中的另一个构造函数

java - 更改 Java 方法中传递的参数值?

scala - Guice Multibinder 不适用于 Play 框架