1. 序章

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

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

2. Mavenのセットアップ

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

<dependency>
    <groupId>com.google.auto.factory</groupId>
    <artifactId>auto-factory</artifactId>
    <version>1.0-beta5</version>
</dependency>

最新バージョンはここにあります。

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 はこのクラスのコンストラクターパラメーターに適用されます。つまり、注釈付きパラメーターは次のようになります。注入されたプロバイダーによって提供されます。

上記のスニペットでは、 Camera が任意のカメラプロデューサーによって提供されることを期待しており、AutoFactoryは次のコードの生成に役立ちます。

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

    private final Provider<Camera> cameraProvider;
    
    @Inject
    PhoneAssemblerFactory(Provider<Camera> 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<String> java_lang_StringProvider;

    @Inject
    public ClassicPhoneFactory(Provider<String> 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<Camera> cameraProvider;
    
    @Inject
    PhoneAssemblerFactory(@Named("Sony") Provider<Camera> 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. 非最終ファクトリー

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

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

    //...

}

今、私たちは持っています:

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

    //...

}

4.3. その他の機能

さらに、 implementing」パラメーターを使用して、生成されたファクトリが実装するインターフェイスのリストを指定できます。

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

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

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

次に、上記のインターフェイスが実装されたファクトリクラスを生成するには、AutoFactoryは基本クラスに関連するコンストラクタを必要とします。

@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;
    }
}

ここでは、extendedを使用してAbstractFactoryクラスを拡張しました。 また、基本抽象クラス(AbstractFactory)の各抽象メソッドには、具象クラス(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. AutoFactory with Guice

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

まず、Guicepom.xmlに追加しましょう。

<dependency>
    <groupId>com.google.inject</groupId>
    <artifactId>guice</artifactId>
    <version>4.2.0</version>
</dependency>

Guice の最新バージョンは、こちらにあります。

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

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

@Generated("com.google.auto.factory.processor.AutoFactoryProcessor")
public final class PhoneFactory {
 
    private final Provider<Camera> cameraProvider;

    @Inject
    public PhoneFactory(@Named("Sony") Provider<Camera> 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. フードの下

AutoFactoryによって提供されるすべての注釈は、コンパイル段階で処理されます 、記事で詳細に説明したように: ソースレベルの注釈処理がどのように機能するか。

7. 結論

この記事では、AutoFactoryの使用方法と、AutoFactoryを Guiceと統合する方法を紹介しました– 書き込みファクトリは反復的でエラーが発生しやすい– AutoFactoryやなどのコード生成ツールX210X] AutoValue を使用すると、時間を大幅に節約でき、微妙なバグから解放されます。

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