Introdução ao AutoFactory
*1. Introdução *
Neste tutorial, daremos uma breve introdução ao AutoFactory, do Google.
Este é um gerador de código no nível da fonte que ajuda a gerar fábricas.
===* 2. Configuração do Maven *
Antes de começarmos, vamos adicionar a seguinte dependência ao _pom.xml: _
<dependency>
<groupId>com.google.auto.factory</groupId>
<artifactId>auto-factory</artifactId>
<version>1.0-beta5</version>
</dependency>
A versão mais recente pode ser encontrada https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22com.google.auto.factory%22%20AND%20a%3A%22auto-factory%22 [aqui].
===* 3. Começo rápido*
Vamos agora dar uma olhada rápida no que AutoFactory pode fazer e criar uma classe Phone simples.
Portanto, quando anotamos a classe Phone com _ @ AutoFactory_ e seu parâmetro construtor com _ @ Provided_, obtemos:
@AutoFactory
public class Phone {
private final Camera camera;
private final String otherParts;
PhoneAssembler(@Provided Camera camera, String otherParts) {
this.camera = camera;
this.otherParts = otherParts;
}
//...
}
Usamos apenas duas anotações: @AutoFactory e @Provided. Quando precisamos de uma fábrica gerada para nossa classe, podemos anotá-la com _ @ AutoFactory, _ enquanto _ @ Fornecido_ se aplica aos parâmetros construtores dessa classe, e significa que o parâmetro anotado deve ser fornecido por um https://docs injetado .oracle.com/javaee/6/api/javax/inject/Provider.html [Provider].
No snippet acima, esperamos que a Camera seja fornecida por qualquer produtor de câmera e AutoFactory ajude a gerar o seguinte código:
@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));
}
//...
}
Agora temos um PhoneFactory gerado automaticamente por AutoFactory em tempo de compilação e podemos usá-lo para produzir instâncias de telefone:
PhoneFactory phoneFactory = new PhoneFactory(
() -> new Camera("Unknown", "XXX"));
Phone simplePhone = phoneFactory.create("other parts");
A anotação _ @ AutoFactory_ também pode ser aplicada aos construtores:
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;
}
//...
}
No snippet acima, aplicamos _ @ AutoFactory_ a ambos os construtores. AutoFactory simplesmente gerará dois métodos de criação para nós adequadamente:
@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_ também suporta parâmetros anotados com _ @ Fornecido_, mas apenas para https://jcp.org/en/jsr/detail?id=330[JSR-330].
Por exemplo, se queremos que o cameraProvider seja “Sony”, podemos alterar a classe Phone para:
@AutoFactory
public class Phone {
PhoneAssembler(
@Provided @Named("Sony") Camera camera, String otherParts) {
this.camera = camera;
this.otherParts = otherParts;
}
//...
}
O AutoFactory manterá os @Named _https://docs.oracle.com/javaee/6/api/javax/inject/Qualifier.html [@Qualifier] _ para que possamos usá-lo, por exemplo, ao usar estruturas de injeção de dependência:
@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. Geração de código personalizado *
Existem vários atributos que podemos usar com a anotação _ @ AutoFactory_ para personalizar o código gerado.
====* 4.1 Nome da classe personalizada *
O nome da classe de fábrica gerada pode ser definido com className :
@AutoFactory(className = "SamsungFactory")
public class SmartPhone {
//...
}
Com a configuração acima, criaremos uma classe chamada SamsungFactory:
@Generated("com.google.auto.factory.processor.AutoFactoryProcessor")
public final class SamsungFactory {
//...
}
====* 4.2 non-final Fábricas *
Observe que a classe de fábrica gerada é marcada como final por padrão, portanto, podemos alterar esse comportamento definindo o allowSubclasses atributo para _false: _
@AutoFactory(
className = "SamsungFactory",
allowSubclasses = true)
public class SmartPhone {
//...
}
Agora temos:
@Generated("com.google.auto.factory.processor.AutoFactoryProcessor")
public class SamsungFactory {
//...
}
====* 4.3 Mais recursos *
Além disso, podemos especificar uma lista de interfaces para a fábrica gerada a ser implementada usando o https://github.com/google/auto/blob/master/factory/src/main/java/com/google/auto/factory/AutoFactory .java # L53 [“] https://github.com/google/auto/blob/master/factory/src/main/java/com/google/auto/factory/AutoFactory.java#L53 [implementando”] parâmetro.
Aqui precisamos do SamsungFactory para produzir smartphones com armazenamento personalizável:
public interface CustomStorage {
SmartPhone customROMInGB(int romSize);
}
*Observe que os métodos na interface devem retornar instâncias da classe base _SmartPhone _.*
*Então, para gerar uma classe de fábrica com a interface acima implementada, _AutoFactory_ requer construtores relevantes na classe base* :
@AutoFactory(
className = "SamsungFactory",
allowSubclasses = true,
implementing = CustomStorage.class)
public class SmartPhone {
public SmartPhone(int romSize){
//...
}
//...
}
Assim, AutoFactory irá gerar o seguinte código:
@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 Fábricas com extensões
Como AutoFactory pode gerar implementações de interface, é natural esperar que ele também possa estender classes e isso é possível:
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;
}
}
Aqui, estendemos a classe AbstractFactory usando extending. Além disso, devemos observar que cada método abstrato na classe abstrata base (AbstractFactory) deve ter um construtor correspondente na classe concreta (_CustomPhone _) .
Por fim, podemos ver o seguinte código gerado:
@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);
}
//...
}
Podemos ver que AutoFactory é inteligente o suficiente para fazer uso do construtor para implementar o método abstrato correspondente - ótimos recursos como este em AutoFactory certamente economizarão muito tempo e código.
*5. AutoFactory com Guice *
Como mencionamos anteriormente neste artigo, o AutoFactory suporta notaçõesJSR-330, para que possamos integrar a estrutura de injeção de dependência existente com isso.
Primeiro, vamos adicionar Guice ao pom.xml:
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>4.2.0</version>
</dependency>
A versão mais recente do Guice pode ser encontrada here
Agora, demonstraremos como o AutoFactory se integra ao Guice.
Como esperamos que a “Sony” seja a fornecedora da câmera, precisamos injetar um SonyCameraProvider no construtor de PhoneFactory:
@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);
}
//...
}
Por fim, criaremos a ligação em um módulo 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++));
}
}
E configuramos o provedor de câmera anotado com _ @ Named (“Sony”) _ em SonyCameraModule para corresponder ao parâmetro do construtor de PhoneFactory.
Agora podemos ver que o Guice está gerenciando a injeção de dependência para nossa fábrica gerada:
Injector injector = Guice.createInjector(new SonyCameraModule());
PhoneFactory injectedFactory = injector.getInstance(PhoneFactory.class);
Phone xperia = injectedFactory.create("Xperia");
===* 6. Sob o capô *
*Todas as anotações fornecidas por _AutoFactory_ são processadas no estágio de compilação* , conforme explicamos em detalhes no artigo: link:/java-annotation-processing-builder [como funciona o processamento de anotações no nível de origem.]
7. Conclusão
Neste artigo, apresentamos como usar o AutoFactory e como integrá-lo às fábricas de gravação Guice – podem ser repetitivas e propensas a erros - ferramentas de geração de código como AutoFactory e link:/Introduction-to-autovalue [AutoValue] can economize muito tempo e nos livre de erros sutis.
Como sempre, a implementação completa dos exemplos de código pode ser encontrada over no Github.