大まかに言うと、ファクトリが予測可能な依存関係を隠して、変化する依存関係のみを指定する必要があることを望みます。Factory のインスタンスを持っている人は、ファクトリや依存関係ではなく、データのみを渡す必要があります。こんなインターフェースをイメージしています。
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 はワーカーをまったく気にしないことに注意してください。ワーカーを作成しないため、質問とは異なり、Shift に渡すためだけに WorkerFactory を受け入れる必要はありません。これは、依存性注入の魅力の一部です。Manager
依存関係の依存関係について中間管理者 (middle-?) を心配する必要はありません。
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 実装を作成します。implement
Manager がインターフェースでない限り、FactoryModuleBuilderでメソッドを呼び出す必要さえありません。次に、どの具象型を作成するかを Guice に指示する必要があります。
キックとニヤニヤ笑いのために、 Google のコード生成 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;
}
// ...
}
ほとんど同じですよね?これにより、Manager クラスとそのコンストラクターを検査し、@Provided アノテーションを読み取り (@Provided は FactoryModuleBuilder の @Assisted の反対です)、その組み合わせでコンストラクターに委譲する Java クラス (読み取り可能なソース コードを含む) が生成されます。パラメータと注入されたフィールドの。Guice だけでなく、Dagger やその他の JSR-330 依存性注入フレームワークと連携する Auto には、他に 2 つの利点があります。
これは、Guice とその FactoryModuleBuilder に反映されていない通常の Java コードです。Android ではリフレクションのパフォーマンスが低いため、Android ではパフォーマンスが大幅に向上する可能性があります。
コード生成では、ManagerFactory インターフェースを作成する必要さえありません。 @AutoFactory へのパラメーターがなければfinal class ManagerFactory { ... }
、Guice が FactoryModuleBuilder を介して接続するのとまったく同じ動作を持つ になります。もちろん、名前とインターフェイスを自分でカスタマイズすることもできます。これは、生成されたコードがツールや IDE に適切に表示されない場合があるため、開発者にも役立つ可能性があります。
コメントに答える更新:
- createWorker について: はい、申し訳ありませんが、コピペ エラーです。
- 自動化について: これは、Assisted Inject も AutoFactory も、静的メソッドに委譲したり、同一のアシスト (ユーザー提供) 引数を持つコンストラクターを操作したりするための優れた方法を持っていないためです。これは、独自の Factory を作成する必要がある場合です。
- Manager が WorkerFactory を必要としないことについて: Manager が WorkerFactory を必要とする唯一の理由は、コンストラクターを呼び出して ShiftFactory または Shift 自体を作成する場合です。私の例では、これらのどちらも行わないことに注意してください。依存関係注入フレームワーク (Guice) に依存関係を提供させています。つまり、Guice が既に提供している ShiftFactory に WorkerFactory が隠されているということです。