Рубрики
Без рубрики

Введение в AutoFactory

Быстрое и практическое введение в Autofactory.

Автор оригинала: Beau Tian.

1. Введение

В этом учебнике, мы дадим краткое введение в АвтоФактория , от Google.

Это генератор кода на уровне источника, который помогает создавать заводы.

2. Мавен Настройка

Прежде чем мы начнем, давайте добавим следующую зависимость к пом.xml:


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

Последнюю версию можно найти здесь .

3. Быстрый старт

Давайте сейчас посмотрим, что АвтоФактория может сделать и создать простой Телефонная класс.

Итак, когда мы аннотировать Телефонная класс с @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 . Когда нам нужна фабрика, созданная для нашего класса, мы можем аннотировать ее @AutoFactory, в то @Provided относится к параметрам конструктора этого класса, а это означает, что аннотированный параметр должен быть предоставлен инъекционным Поставщик .

В фрагменте выше, мы ожидаем, что Оператор быть предоставлены любым производителем камеры и АвтоФактория поможет создать следующий код:

@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));
    }
    
    // ...

}

Теперь у нас есть ТелефонФактория генерируется автоматически АвтоФактория во время компиляции, и мы можем использовать его для создания экземпляров телефона:

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 обоим конструкторам. АвтоФактория будет просто генерировать два метода создания для нас соответственно:

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

    //...

}

Автофактория также поддерживает параметры, аннотированные @Provided , но только для JSR-330 Аннотации.

Например, если мы хотим камераПровид быть “Sony”, мы можем изменить Телефонная от класса до:

@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. Неокордные фабрики

Обратите внимание, что генерируемый класс фабрики по умолчанию помечается как окончательный, поэтому мы можем изменить это поведение, установив позволяютСубклассы атрибут к ложный:

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

    //...

}

Теперь у нас есть:

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

    //...

}

4.3. Больше возможностей

Кроме того, мы можем указать список интерфейсов для генерируемого завода для реализации с помощью реализации” параметр.

Здесь нам нужна SamsungFactory для производства смартфонов с настраиваемым хранилищем:

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

Обратите внимание, что методы в интерфейсе должны возвращать экземпляры базового класса смартфон .

Затем, чтобы создать заводской класс с интерфейсом выше реализованы, АвтоФактория требуется соответствующие конструкторы в базовом классе :

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

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

    //...

}

Таким образом, АвтоФактория будет генерировать следующий код:

@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. Фабрики с расширениями

С АвтоФактория может генерировать реализации интерфейса, естественно ожидать, что он сможет расширить классы, а также, и это возможно действительно:

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

Здесь мы продлили АбстрактФактория класс с использованием расширение . Кроме того, мы должны обратите внимание, что каждый абстрактный метод в базовом абстрактном классе ( АбстрактФактория ) должен иметь соответствующего конструктора в конкретном классе ( Пользовательские телефоны ) .

Наконец, мы можем увидеть следующий генерируемый код:

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

    //...

}

Мы видим, что АвтоФактория достаточно умен, чтобы использовать конструктора для реализации соответствующего абстрактного метода – большие возможности, как это АвтоФактория безусловно, сэкономит нам много времени и кода.

5. Автофабрика с Guice

Как мы упоминали ранее в этой статье, АвтоФактория поддерживает Аннотации JSR-330 , таким образом, мы можем интегрировать существующую инфраструктуру впрыска зависимости с ней.

Во-первых, давайте добавим Гойс в пом.xml :


    com.google.inject
    guice
    4.2.0

Последняя версия Гойс можно найти здесь .

Теперь мы продемонстрируем, насколько хорошо АвтоФактория интегрируется с Гойс .

Как мы ожидаем, “Sony”, чтобы быть поставщиком камеры, мы должны ввести SonyCameraПровидятель ТелефонФактория Конструктор:

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

    //...

}

Наконец, мы сделаем привязку в Гойс модуль:

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++));
    }

}

И мы установили камеру провайдера аннотированный с @Named (“Сони”) в SonyCameraModule чтобы соответствовать ТелефонФактория Параметр конструктора.

Теперь мы видим, что Гойс управляет инъекцией зависимости для нашего генерируемого завода:

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

6. Под капотом

Все аннотации, предоставленные АвтоФактория обрабатываются на этапе компиляции , как мы подробно объяснили в статье: как работает обработка аннотации на уровне источника.

7. Заключение

В этой статье мы представили, как использовать AutoFactory, и как интегрировать его с Гойс — фабрики по написанию могут быть повторяющимися и подверженными ошибкам инструментами генерации кода, такими как АвтоФактория и Автовалю может сэкономить нам много времени и освободить нас от тонких ошибок.

Как всегда, полную реализацию образцов кода можно найти более на Github .