@Assisted \ @Provider 在分层设计中对象创建中的使用

@Assisted \ @Provider usage in objects creation in hierarchical design

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

我参考的当前设计是这样的: 层次结构顶部的class也是暴露给客户端的只有class(基本上就是publicAPI), 它看起来像这样:

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

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

class Manager 创建 class Shift 的实例。 class Shift 创建 class Worker.

的实例

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

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

shiftIdManager 提供,其余是来自 Manager 的相同对象' s 构造函数。

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

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

workerIdShift class 提供。其余部分委托给 Shift 构造函数。

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

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

谢谢

在高层次上,您希望工厂隐藏可预测的依赖关系,这样您只需指定发生变化的依赖关系。拥有工厂实例的人应该只需要传递数据,而不是工厂或依赖项。我把界面想象成这样

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 根本不关心工人——它不会创建工人,因此与您的问题不同,您不必接受 WorkerFactory 只是为了将其传递给您的 Shift。这是依赖注入的吸引力的一部分;您不必担心中间经理(middle-Manager?)及其依赖项的依赖项。

另请注意,none 的 Factory 接口或实现甚至对构造函数之外的 public API 稍微可见。这些是实现细节,您可以遵循对象层次结构而无需从外部调用。

现在,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());
  }
}

...但这主要是样板文件,当有大量注入或非注入参数时可能更是如此。相反,只要您仍然提供 ManagerFactory 接口并注释构造函数,Guice 就可以为您完成:

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 方法的 return 类型,将其与 @Inject 和 @Assisted 注释以及接口方法参数相匹配,并从那里计算出来,从而创建自己的基于反射的 ManagerFactory 实现。您甚至不需要在 FactoryModuleBuilder 上调用 implement 方法,除非 Manager 是一个接口;那么你必须告诉 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 class(带有您可以阅读的源代码!)检查管理器 class 及其构造函数,读取 @Provided 注释(n.b。@Provided与 FactoryModuleBuilder 的 @Assisted 相反),并使用其参数和注入字段的组合委托给构造函数。 Auto 的另外两个优点,它与 Guice 以及 Dagger 和其他 JSR-330 依赖注入框架一起工作:

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

  2. 有了代码生成,您甚至不需要创建 ManagerFactory 接口——如果没有任何参数给@AutoFactory,您最终会得到一个 final class ManagerFactory { ... },其行为与 Guice 完全相同将通过 FactoryModuleBuilder 进行连接。当然,您可以自己自定义名称和接口,这也可能对您的开发人员有所帮助,因为生成的代码有时在工具和 IDE 中显示效果不佳。


更新以回答评论:

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