在高层次上,您希望您的工厂隐藏可预测的依赖项,因此您只需指定更改的依赖项。拥有工厂实例的人应该只需要传入数据,而不是工厂或依赖项。我画的界面是这样的。
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 是一个接口,否则您甚至不需要调用implement
FactoryModuleBuilder 上的方法;那么你必须告诉 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 依赖注入框架一起使用:
这是在 Guice 及其 FactoryModuleBuilder 中没有反射的普通 Java 代码;Android 上的反射性能很差,所以这可能是一个不错的性能提升。
使用代码生成,您甚至不需要创建 ManagerFactory 接口——如果没有任何 @AutoFactory 参数,您最终会得到一个final class ManagerFactory { ... }
与 Guice 将通过 FactoryModuleBuilder 连接起来的行为完全相同的接口。当然,您可以自己自定义名称和接口,这也可能对您的开发人员有所帮助,因为生成的代码有时在工具和 IDE 中看起来并不好。
更新以回答评论:
- 关于 createWorker:是的,抱歉,复制粘贴错误。
- 关于自动化:这是因为 Assisted Inject 和 AutoFactory 都没有很好的方式来委派静态方法,或者使用具有相同辅助(用户提供)参数的构造函数。在这种情况下,您可能必须编写自己的工厂。
- 关于不需要 WorkerFactory 的 Manager:Manager 需要 WorkerFactory 的唯一原因是它是否通过调用构造函数来创建 ShiftFactory 或 Shift 本身。请注意,我的示例两者都没有:您让依赖注入框架 (Guice) 提供依赖项,这意味着 WorkerFactory 隐藏在 Guice 已经提供的 ShiftFactory 中。