1

这个问题是关于正确使用 Guice @Assisted 和 @Provides 以及如何做到这一点。

我指的当前设计是这样的:层次结构顶部的类也是唯一暴露给客户端的类(基本上是公共 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);

Manager提供的shiftId和其余的是来自Manager的构造函数的相同对象。

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

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

Shift类提供的workerId 。其余的由Shift构造函数委托。

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

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

谢谢

4

1 回答 1

2

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

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?)及其依赖项的依赖项。

另请注意,Factory在构造函数之外,您的公共 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 方法的返回类型,将其与 @Inject 和 @Assisted 注释以及接口方法参数匹配,并从那里找出它来创建自己的基于反射的 ManagerFactory 实现。除非 Manager 是一个接口,否则您甚至不需要调用implementFactoryModuleBuilder 上的方法;那么你必须告诉 Guice 要创建哪种具体类型。


对于踢球和咧嘴笑,让我们看看谷歌的代码生成 AutoFactory 包同样的事情:

@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 注释(nb @Provided 与 FactoryModuleBuilder 的 @Assisted 相反),并将其组合委托给构造函数参数和注入字段。Auto 的另外两个优势,它与 Guice 以及 Dagger 和其他 JSR-330 依赖注入框架一起使用:

  1. 这是在 Guice 及其 FactoryModuleBuilder 中没有反射的普通 Java 代码;Android 上的反射性能很差,所以这可能是一个不错的性能提升。

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


更新以回答评论:

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