AutoFactoryの紹介

AutoFactoryの概要

1. 前書き

このチュートリアルでは、GoogleによるAutoFactoryについて簡単に紹介します。

これは、ファクトリの生成に役立つソースレベルのコードジェネレーターです。

2. Mavenセットアップ

始める前に、次の依存関係をpom.xml:に追加しましょう


    com.google.auto.factory
    auto-factory
    1.0-beta5

最新バージョンはhereで見つけることができます。

3. クイックスタート

ここで、AutoFactoryで何ができるかを簡単に見て、単純なPhoneクラスを作成しましょう。

したがって、Phoneクラスに@AutoFactoryで注釈を付け、そのコンストラクターパラメーターに@Providedで注釈を付けると、次のようになります。

@AutoFactory
public class Phone {

    private final Camera camera;

    private final String otherParts;

    PhoneAssembler(@Provided Camera camera, String otherParts) {
        this.camera = camera;
        this.otherParts = otherParts;
    }

    //...

}

@AutoFactory@Providedの2つの注釈のみを使用しました。 クラス用に生成されたファクトリが必要な場合は、@AutoFactory,でアノテーションを付けることができますが、@Providedはこのクラスのコンストラクタパラメータに適用されます。つまり、アノテーションが付けられたパラメータは、注入された%(t4 )s。

上記のスニペットでは、Cameraはすべてのカメラプロデューサーによって提供されると予想され、AutoFactoryは次のコードの生成に役立ちます。

@Generated("com.google.auto.factory.processor.AutoFactoryProcessor")
public final class PhoneFactory {

    private final Provider cameraProvider;

    @Inject
    PhoneAssemblerFactory(Provider cameraProvider) {
        this.cameraProvider = checkNotNull(cameraProvider, 1);
    }

    PhoneAssembler create(String otherParts) {
      return new PhoneAssembler(
        checkNotNull(cameraProvider.get(), 1),
        checkNotNull(otherParts, 2));
    }

    // ...

}

これで、コンパイル時にAutoFactoryによって自動的に生成されたPhoneFactoryができました。これを使用して、電話インスタンスを生成できます。

PhoneFactory phoneFactory = new PhoneFactory(
  () -> new Camera("Unknown", "XXX"));
Phone simplePhone = phoneFactory.create("other parts");

@AutoFactoryアノテーションは、コンストラクターにも適用できます。

public class ClassicPhone {

    private final String dialpad;
    private final String ringer;
    private String otherParts;

    @AutoFactory
    public ClassicPhone(
      @Provided String dialpad, @Provided String ringer) {
        this.dialpad = dialpad;
        this.ringer = ringer;
    }

    @AutoFactory
    public ClassicPhone(String otherParts) {
        this("defaultDialPad", "defaultRinger");
        this.otherParts = otherParts;
    }

    //...

}

上記のスニペットでは、両方のコンストラクターに@AutoFactoryを適用しました。 AutoFactoryは、それに応じて2つの作成メソッドを生成するだけです。

@Generated(value = "com.google.auto.factory.processor.AutoFactoryProcessor")
public final class ClassicPhoneFactory {
    private final Provider java_lang_StringProvider;

    @Inject
    public ClassicPhoneFactory(Provider java_lang_StringProvider) {
        this.java_lang_StringProvider =
          checkNotNull(java_lang_StringProvider, 1);
    }

    public ClassicPhone create() {
        return new ClassicPhone(
          checkNotNull(java_lang_StringProvider.get(), 1),
          checkNotNull(java_lang_StringProvider.get(), 2));
    }

    public ClassicPhone create(String otherParts) {
        return new ClassicPhone(checkNotNull(otherParts, 1));
    }

    //...

}

AutoFactoryは、@Providedアノテーションが付けられたパラメータもサポートしますが、JSR-330アノテーションのみがサポートされます。

たとえば、cameraProviderを「Sony」にしたい場合は、Phoneクラスを次のように変更できます。

@AutoFactory
public class Phone {

    PhoneAssembler(
      @Provided @Named("Sony") Camera camera, String otherParts) {
        this.camera = camera;
        this.otherParts = otherParts;
    }

    //...

}

AutoFactoryは@Named@Qualifierを保持するので、たとえば、依存性注入フレームワークを使用するときにそれを利用できます。

@Generated("com.google.auto.factory.processor.AutoFactoryProcessor")
public final class PhoneFactory {

    private final Provider cameraProvider;

    @Inject
    PhoneAssemblerFactory(@Named("Sony") Provider cameraProvider) {
      this.cameraProvider = checkNotNull(cameraProvider, 1);
    }

    //...

}

4. カスタマイズされたコード生成

生成されたコードをカスタマイズするために@AutoFactoryアノテーションで使用できる属性がいくつかあります。

4.1. カスタムクラス名

生成されたファクトリクラスの名前は、classNameで設定できます。

@AutoFactory(className = "SamsungFactory")
public class SmartPhone {

    //...

}

上記の構成で、SamsungFactoryという名前のクラスを作成します。

@Generated("com.google.auto.factory.processor.AutoFactoryProcessor")
public final class SamsungFactory {

    //...

}

4.2. non-finalファクトリ

生成されたファクトリクラスはデフォルトでfinalとマークされているため、allowSubclasses属性をfalse:に設定することでこの動作を変更できることに注意してください。

@AutoFactory(
  className = "SamsungFactory",
  allowSubclasses = true)
public class SmartPhone {

    //...

}

今我々は持っています:

@Generated("com.google.auto.factory.processor.AutoFactoryProcessor")
public class SamsungFactory {

    //...

}

4.3. その他の機能

さらに、https://github.com/google/auto/blob/master/factory/src/main/java/com/google/auto/factory/AutoFactory.java#L53implementing” parameter.を使用して、生成されたファクトリが実装するインターフェイスのリストを指定できます。

ここでは、カスタマイズ可能なストレージを備えたスマートフォンを製造するためにSamsungFactoryが必要です。

public interface CustomStorage {
    SmartPhone customROMInGB(int romSize);
}

インターフェイスのメソッドは、基本クラスSmartPhoneのインスタンスを返す必要があることに注意してください。

Then, to generate a factory class with the interface above implemented, AutoFactory requires relevant constructors in the base class

@AutoFactory(
  className = "SamsungFactory",
  allowSubclasses = true,
  implementing = CustomStorage.class)
public class SmartPhone {

    public SmartPhone(int romSize){
        //...
    }

    //...

}

したがって、AutoFactoryは次のコードを生成します。

@Generated("com.google.auto.factory.processor.AutoFactoryProcessor")
public class SamsungFactory implements CustomStorage {

    //...

    public SmartPhone create(int romSize) {
        return new SmartPhone(romSize);
    }

    @Override
    public SmartPhone customROMInGB(int romSize) {
        return create(romSize);
    }
}

4.4. 拡張機能を備えた工場

AutoFactoryはインターフェースの実装を生成できるため、クラスも拡張できると期待するのは自然なことであり、これは確かに可能です。

public abstract class AbstractFactory {
    abstract CustomPhone newInstance(String brand);
}

@AutoFactory(extending = AbstractFactory.class)
public class CustomPhone {

    private final String brand;

    public CustomPhone(String brand) {
        this.brand = brand;
    }
}

ここでは、extendingを使用してAbstractFactoryクラスを拡張しました。 また、note that each abstract method in the base abstract class (AbstractFactory) should have a corresponding constructor in the concrete class (CustomPhone)にする必要があります。

最後に、生成された次のコードを確認できます。

@Generated("com.google.auto.factory.processor.AutoFactoryProcessor")
public final class CustomPhoneFactory extends AbstractFactory {

    @Inject
    public CustomPhoneFactory() {
    }

    public CustomPhone create(String brand) {
        return new CustomPhone(checkNotNull(brand, 1));
    }

    @Override
    public CustomPhone newInstance(String brand) {
        return create(brand);
    }

    //...

}

AutoFactoryは、コンストラクターを使用して対応する抽象メソッドを実装するのに十分スマートであることがわかります。AutoFactoryのこのような優れた機能により、多くの時間とコードを確実に節約できます。

5. AutoFactoryGuice

この記事の前半で述べたように、AutoFactoryJSR-330 annotationsをサポートしているため、既存の依存性注入フレームワークを統合できます。

まず、pom.xmlGuiceを追加しましょう。


    com.google.inject
    guice
    4.2.0

Guiceの最新バージョンはhereにあります。

次に、AutoFactoryGuiceとどの程度うまく統合されているかを示します。

「Sony」がカメラプロバイダーであることが期待されるため、SonyCameraProviderPhoneFactoryのコンストラクターに挿入する必要があります。

@Generated("com.google.auto.factory.processor.AutoFactoryProcessor")
public final class PhoneFactory {

    private final Provider cameraProvider;

    @Inject
    public PhoneFactory(@Named("Sony") Provider cameraProvider) {
        this.cameraProvider = checkNotNull(cameraProvider, 1);
    }

    //...

}

最後に、Guiceモジュールでバインディングを作成します。

public class SonyCameraModule extends AbstractModule {

    private static int SONY_CAMERA_SERIAL = 1;

    @Named("Sony")
    @Provides
    Camera cameraProvider() {
        return new Camera(
          "Sony", String.format("%03d", SONY_CAMERA_SERIAL++));
    }

}

また、SonyCameraModule@Named(“Sony”)でアノテーションが付けられたカメラプロバイダーを、PhoneFactoryのコンストラクターパラメーターと一致するように設定します。

これで、Guiceが生成​​されたファクトリの依存性注入を管理していることがわかります。

Injector injector = Guice.createInjector(new SonyCameraModule());
PhoneFactory injectedFactory = injector.getInstance(PhoneFactory.class);
Phone xperia = injectedFactory.create("Xperia");

6. フードの下

記事で詳細に説明したように、All annotations provided by AutoFactory are processed in the compilation stagehow the source-level annotation processing works.

7. 結論

この記事では、AutoFactoryの使用方法と、AutoFactoryをGuice –書き込みファクトリと統合する方法を紹介しました。繰り返しが発生し、エラーが発生しやすくなります。AutoFactoryAutoValueなどのコード生成ツールでは多くの時間を節約し、微妙なバグから解放してください。

いつものように、コードサンプルの完全な実装はover on Githubにあります。