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

大まかに言うと、ファクトリが予測可能な依存関係を隠して、変化する依存関係のみを指定する必要があることを望みます。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 実装を作成します。implementManager がインターフェースでない限り、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 つの利点があります。

  1. これは、Guice とその FactoryModuleBuilder に反映されていない通常の Java コードです。Android ではリフレクションのパフォーマンスが低いため、Android ではパフォーマンスが大幅に向上する可能性があります。

  2. コード生成では、ManagerFactory インターフェースを作成する必要さえありません。 @AutoFactory へのパラメーターがなければfinal class ManagerFactory { ... }、Guice が FactoryModuleBuilder を介して接続するのとまったく同じ動作を持つ になります。もちろん、名前とインターフェイスを自分でカスタマイズすることもできます。これは、生成されたコードがツールや IDE に適切に表示されない場合があるため、開発者にも役立つ可能性があります。


コメントに答える更新:

  • createWorker について: はい、申し訳ありませんが、コピペ エラーです。
  • 自動化について: これは、Assisted Inject も AutoFactory も、静的メソッドに委譲したり、同一のアシスト (ユーザー提供) 引数を持つコンストラクターを操作したりするための優れた方法を持っていないためです。これは、独自の Factory を作成する必要がある場合です。
  • Manager が WorkerFactory を必要としないことについて: Manager が WorkerFactory を必要とする唯一の理由は、コンストラクターを呼び出して ShiftFactory または Shift 自体を作成する場合です。私の例では、これらのどちらも行わないことに注意してください。依存関係注入フレームワーク (Guice) に依存関係を提供させています。つまり、Guice が既に提供している ShiftFactory に WorkerFactory が隠されているということです。
于 2016-11-04T02:27:32.567 に答える