@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);
shiftId 由 Manager 提供,其余是来自 Manager 的相同对象' s 构造函数。
为了创建 Worker 我们使用了 2 个静态工厂方法(但它可以改变..):
public Worker createWorkerTypeA(int shiftId, int workerId)
public Worker createWorkerTypeB(int shiftId, int workerId)
workerId 由 Shift 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 依赖注入框架一起工作:
这是正常的 Java 代码,没有 Guice 及其 FactoryModuleBuilder 中的反射; Android 上的反射性能很差,因此这可能是一个不错的性能提升。
有了代码生成,您甚至不需要创建 ManagerFactory 接口——如果没有任何参数给@AutoFactory,您最终会得到一个 final class ManagerFactory { ... }
,其行为与 Guice 完全相同将通过 FactoryModuleBuilder 进行连接。当然,您可以自己自定义名称和接口,这也可能对您的开发人员有所帮助,因为生成的代码有时在工具和 IDE 中显示效果不佳。
更新以回答评论:
- 关于 createWorker:是的,抱歉,复制粘贴错误。
- 关于自动化:这是因为 Assisted Inject 和 AutoFactory 都没有很好的方法来委托静态方法,或者使用具有相同辅助(用户提供)参数的构造函数。在这种情况下,您可能必须编写自己的工厂。
- 关于不需要 WorkerFactory 的 Manager:Manager 需要 WorkerFactory 的唯一原因是它是否通过调用构造函数创建 ShiftFactory 或 Shift 本身。请注意,我的示例没有执行这些操作:您让依赖项注入框架 (Guice) 提供依赖项,这意味着 WorkerFactory 隐藏在 Guice 已经提供的 ShiftFactory 中。
这个问题是关于 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);
shiftId 由 Manager 提供,其余是来自 Manager 的相同对象' s 构造函数。
为了创建 Worker 我们使用了 2 个静态工厂方法(但它可以改变..):
public Worker createWorkerTypeA(int shiftId, int workerId)
public Worker createWorkerTypeB(int shiftId, int workerId)
workerId 由 Shift 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 依赖注入框架一起工作:
这是正常的 Java 代码,没有 Guice 及其 FactoryModuleBuilder 中的反射; Android 上的反射性能很差,因此这可能是一个不错的性能提升。
有了代码生成,您甚至不需要创建 ManagerFactory 接口——如果没有任何参数给@AutoFactory,您最终会得到一个
final class ManagerFactory { ... }
,其行为与 Guice 完全相同将通过 FactoryModuleBuilder 进行连接。当然,您可以自己自定义名称和接口,这也可能对您的开发人员有所帮助,因为生成的代码有时在工具和 IDE 中显示效果不佳。
更新以回答评论:
- 关于 createWorker:是的,抱歉,复制粘贴错误。
- 关于自动化:这是因为 Assisted Inject 和 AutoFactory 都没有很好的方法来委托静态方法,或者使用具有相同辅助(用户提供)参数的构造函数。在这种情况下,您可能必须编写自己的工厂。
- 关于不需要 WorkerFactory 的 Manager:Manager 需要 WorkerFactory 的唯一原因是它是否通过调用构造函数创建 ShiftFactory 或 Shift 本身。请注意,我的示例没有执行这些操作:您让依赖项注入框架 (Guice) 提供依赖项,这意味着 WorkerFactory 隐藏在 Guice 已经提供的 ShiftFactory 中。