Factory com CDI
Autor: Luciano Witt | Publicado: 2013-07-24T18:09:00-03:00

Neste artigo, a partir de um exemplo simples de fábrica (Factory) na linguagem Java, vou &nbsp;mostrar como implementar esta mesma fábrica, porém para criar objetos gerenciados pelo&nbsp;container, usando CDI.

Como exemplo vou usar uma fábrica de formas que, dado um tipo de forma, retorna uma instância deste tipo.

Para este exemplo teremos duas formas, o retângulo e o círculo, listadas na enumeração&nbsp;ShapeType.

public enum ShapeType {
   RECTANGLE, CIRCLE
}


A classe abstrata Shape&nbsp;representa uma forma genérica.&nbsp;Shape&nbsp;possui uma dependência do tipo&nbsp;ShapeDependency, que deve ser passada como parâmetro para o seu construtor.

public abstract class Shape {

   private ShapeDependency dependency;

   public Shape(ShapeDependency dependency) {
      this.dependency = dependency;
   }

   public ShapeDependency getDependency() {
      return dependency;
   }
}


A classe&nbsp;ShapeDependency&nbsp;por sua vez, possui a propriedade&nbsp;name, a qual deve ser &nbsp;passada como parâmetro para o seu construtor.

public class ShapeDependency {

   private String name;

   public ShapeDependency(String name) {
      this.name = name;
   }

   public String getName() {
      return name;
   }
}


As classes&nbsp;Rectangle&nbsp;e&nbsp;Circle&nbsp;são formas específicas, subclasses de&nbsp;Shape. Elas instanciam&nbsp;ShapeDependency&nbsp;nos seus construtores e passam esta instância para o construtor da superclasse&nbsp;Shape, já recebendo o name correto.

public class Rectangle extends Shape {

   public Rectangle() {
      super(new ShapeDependency("Rectangle dependency"));
   }
}


public class Circle extends Shape {

   public Circle() {
      super(new ShapeDependency("Circle dependency"));
   }
}


Por fim, a fábrica&nbsp;ShapeFactory&nbsp;possui o método&nbsp;create&nbsp;que retorna uma instância da classe&nbsp;Shape, de acordo com o&nbsp;ShapeType&nbsp;recebido por parâmetro.

public class ShapeFactory {

   public Shape create(ShapeType type) {
      if(RECTANGLE.equals(type)){
         return new Rectangle();
      }
      if(CIRCLE.equals(type)){
         return new Circle();
      }
      return null;
   }
}


Até aqui tudo tranquilo, nada diferente de uma fábrica comum. Mas e se a instância retornada pela fábrica possui uma dependência gerenciada pelo container?

Para ilustrar este caso,&nbsp;Shape&nbsp;terá a sua dependência injetada por CDI.

public abstract class Shape {

   @Inject
   private ShapeDependency dependency;

   public ShapeDependency getDependency() {
      return dependency;
   }
}


Rectangle&nbsp;e&nbsp;Circle&nbsp;teriam de configurar a dependência num método anotado com @PostConstruct, pois este será executado pelo container após a injeção da dependência, ao contrário do construtor, que é executado antes, o que ocasionaria NullPointerException.

public class Rectangle extends Shape {

   @PostConstruct
   public void setUp() {
      dependency.setName("Rectangle dependency");
   }
}


public class Circle extends Shape {

   @PostConstruct
   public void setUp() {
      dependency.setName("Circle dependency");
   }
}


E&nbsp;ShapeDependency&nbsp;teria o valor da propriedade&nbsp;name configurado num setter, pois o container usa apenas o construtor vazio (sem parâmetros) para instanciar as classes que ele gerencia.

public class ShapeDependency {

   private String name;

   public void setName(String name) {
      this.name = name;
   }

   public String getName() {
      return name;
   }
}


Neste novo contexto, o problema é que o método&nbsp;create&nbsp;de&nbsp;ShapeFactory&nbsp;não pode mais criar a instância de&nbsp;Shape&nbsp;com um comando new, pois esta nova instância não terá sua dependência injetada, já que com este comando é você que está gerenciando o ciclo de vida da instância, não o container.

Você também não pode simplesmente injetar&nbsp;Shape&nbsp;na&nbsp;ShapeFactory, pois&nbsp;Shape&nbsp;é uma classe abstrata e o container não saberia qual subclasse injetar,&nbsp;Rectangle&nbsp;ou&nbsp;Circle.

E mesmo que o container injetasse uma delas arbitrariamente, ele o faria ao instanciar&nbsp;ShapeFactory, e nós só sabemos de qual subclasse de Shape queremos uma instância durante a execução do método create, a partir do parâmetro ShapeType recebido.

A solução é usar um recurso que o CDI nos proporciona, que permite avisar ao container que ele não deve injetar a dependência (Shape) ao instanciar&nbsp;ShapeFactory, que é a interface Instance&lt;T&gt;. Esta interface ainda nos permite, no momento apropriado,&nbsp;selecionar a subclasse desejada através do método&nbsp;select, e obter a instância desta classe através do método&nbsp;get.

public class ShapeFactory {

   @Any
   @Inject
   private Instance&lt;Shape&gt; shape;

   public Shape create(ShapeType type) {
      if(RECTANGLE.equals(type)){
         return shape.select(Rectangle.class).get();
      }
      if(CIRCLE.equals(type)){
         return shape.select(Circle.class).get();
      }
      return null;
   }
}



Desta forma temos uma fábrica com o mesmo comportamento da primeira, mas que é capaz de entregar instâncias gerenciadas pelo container.

PS: Note que para funcionar a própria fábrica deve ser gerenciada pelo&nbsp;container.