这个问题是关于Guice@Assisted和@Providers的正确用法,以及如何做到这一点。
我所指的当前设计是这样的:层次结构顶部的类也是暴露给客户端(基本上是公共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,我们使用了两种静态工厂方法(但可以更改..(:
public Worker createWorkerTypeA(int shiftId, int workerId)
public Worker createWorkerTypeB(int shiftId, int workerId)
workerId由Shift类提供。其余部分由Shift构造函数委托。
正确的,桂式的方法是什么?我应该把@Assisted放在哪里@提供?
我真的很想要一个这样的代码示例,包括抽象模块,因为到目前为止我看到的代码exmaples对我来说还不能理解。
感谢
在高层,您希望工厂隐藏可预测的依赖关系,这样您只需要指定更改的依赖关系。拥有工厂实例的人应该只传递数据,而不是工厂或依赖项。我把界面想象成这样。
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);
}
}
请注意,经理根本不关心工人——它不会创造他们,所以与您的问题不同,您不必接受WorkerFactory,只需将其传递给您的轮班。这是依赖注入的吸引力的一部分;您不必关心中间管理器(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());
}
}
但这在很大程度上是样板,当有很多注入或未注入的参数时,情况可能会更糟。相反,Guice可以为您做到这一点,只要您仍然提供ManagerFactory接口,并对构造函数进行注释:
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创建了自己的基于反射的ManagerFactory实现,方法是读取Manager方法的返回类型,将其与@Inject和@Assisted注释以及接口方法参数相匹配,然后从中进行计算。您甚至不需要在FactoryModuleBuilder上调用implement
方法,除非Manager是一个接口;那么你必须告诉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注释(n.b.@Provide与FactoryModuleBuilder的@Assisted相反(,并通过其参数和注入字段的组合委托给构造函数。Auto的另外两个优点是,它与Guice、Dagger和其他JSR-330依赖注入框架一起工作:
这是在Guice及其FactoryModuleBuilder中无反射的普通Java代码;反射性能在Android上很差,所以这可以在那里获得不错的性能提升。
有了代码生成,你甚至不需要创建一个ManagerFactory接口——如果没有@AutoFactory的任何参数,你最终会得到一个
final class ManagerFactory { ... }
,它的行为与Guice通过FactoryModuleBuilder连接的行为完全相同。当然,您可以自己自定义名称和接口,这也可能有助于您的开发人员,因为生成的代码有时对工具和IDE来说不太好。
更新以回答评论:
- 关于createWorker:是的,对不起,复制粘贴错误
- 关于自动化:这是因为辅助注入和AutoFactory都没有很好的方法来委托给静态方法,或者使用具有相同辅助(用户提供(参数的构造函数。在这种情况下,你可能不得不写一个自己的工厂
- 关于Manager不需要WorkerFactory:Manager要求WorkerFactory的唯一原因是,如果它通过调用构造函数来创建ShiftFactory或Shift本身。请注意,我的示例没有做到这两点:您让依赖项注入框架(Guice(提供依赖项,这意味着WorkerFactory隐藏在Guice已经提供的ShiftFactory中