DESIGN PATTERNS – ABSTRACT FACTORY

O padrão de projeto Abstract Factory define uma interface para criar famílias de objetos sem especificar suas classes concretas.

Veja o seu diagrama de classes e código Java que implementam o padrão:

Figura 1 – Abstract Factory
public interface ProdutoAbstrato {
}

public class ProdutoConcretoA implements ProdutoAbstrato {
}

public class ProdutoConcretoB implements ProdutoAbstrato {
}

public abstract class FabricaAbstrata {

  public abstract ProdutoAbstrato criarProduto();

}

public class FabricaConcretaA extends FabricaAbstrata {

  public ProdutoAbstrato criarProduto() {
    return new ProdutoConcretoA();
  }

}

public class FabricaConcretaB extends FabricaAbstrata {

  public ProdutoAbstrato criarProduto() {
    return new ProdutoConcretoB();
  }

}

Listagem 1 – Abstract Factory

Os elementos participantes desse padrão são:

FabricaAbstrata

Como o nome já diz, é uma “Fábrica Abstrata”. Pode ser uma classe — de preferência abstrata, ou uma interface. Seu objetivo é declarar métodos de criação de objetos do tipo ProdutoAbstrato, que são implementados por uma classe do tipo FabricaConcreta, que estende ou implementa a FabricaAbstrata.

FabricaConcreta

Estende ou implementa a FabricaAbstrata. Podem ser definidas várias classes desse tipo, de acordo com o cenário do problema de projeto a ser resolvido. Na verdade só da necessidade de se utilizar esse padrão, pelo menos duas classes desse tipo são definidas. Aqui o esforço é implementar os métodos declarados em FabricaAbstrata, criando um objeto do tipo ProdutoConcreto e retornando-o como um ProdutoAbstrato (polimorfismo!). Veremos logo mais, que é comum existir mais de uma classe do tipo ProdutoConcreto assim como ocorre com a classe do tipo FabricaConcreta. Na verdade, a quantidade de classes do tipo FabricaConcreta está diretamente ligada com a quantidade de classes do tipo ProdutoConcreto.

ProdutoAbstrato

Pode ser uma classe — de preferência abstrata, ou uma interface. Declara os métodos que são implementados por classes do tipo ProdutoConcreto. Como já explicado, um método de FabricaConcreta, cria internamente um objeto do tipo ProdutoConcreto mas esse objeto é retornado como um ProdutoAbstrato. Um objeto consumidor de Abstract Factory, não sabe qual ProdutoConcreto está sendo criado — e nem precisa saber —, mas sabe quais métodos do produto ele pode utilizar, e esses métodos são definidos aqui!

ProdutoConcreto

Estende ou implementa a classe ProdutoAbstrato. Nessa classe são implementados os métodos declarados em ProdutoAbstrato. Pode-se dizer que essa classe é a causa de toda essa brincadeira. Pensando em um cenário real, é o conteúdo dessa classe que estaria fazendo algo em algum lugar em um aplicativo, se não estivesse sendo aplicado esse padrão de projeto. Como já foi dito antes, é comum existir mais de uma classe do tipo ProdutoConcreto no projeto. Essa quantidade está diretamente ligada com a quantidade de classes do tipo FabricaConcreta: para cada FabricaConcreta, há pelo menos um ProdutoConcreto.

Resolvendo um Problema de Projeto Utilizando o Padrão de Projeto Abstract Factory

Os conceitos apresentados acima são muito abstratos (risos…). É possível que sem a apresentação de um exemplo de uso prático, ficaria difícil de entender como tudo isso funciona e o porquê de se utilizar toda essa arquitetura. Sendo assim, vou apresentar um problema de arquitetura de projeto de software e uma forma de como resolvê-lo utilizando o padrão de projeto Abstract Factory.

Imagine que você está projetando um novo aplicativo e uma das primeiras coisas a serem feitas é definir como vai ser a arquitetura da camada de conexão com o banco de dados. Nesse cenário, o aplicativo deverá suportar conexão com os bancos de dados Oracle e SQLServer. A premissa básica é que o aplicativo deverá se conectar a um banco de dados e a informação de qual banco de dados o aplicativo deverá se conectar estará definida em um arquivo de configuração. Oracle e SQLServer possuem particularidades de conexão e elas deverão ser consideradas — não será possível se conectar aos dois bancos da mesma forma!

A várias soluções para esse tipo de problema; resolvi de uma forma, utilizando Abstract Factory.

Veja o diagrama de classes e código Java que implementam a solução:

Figura 2 – Abstract Factory
public interface ConectorAbstrato {

  public void conectar();

}

public class ConectorOracle implements ConectorAbstrato {

  public void conectar() {
    // Realiza a conexão com o banco de dados Oracle
  }

}

public class ConectorSQLServer implements ConectorAbstrato {

  public void conectar() {
    // Realiza a conexão com o banco de dados SQLServer
  }

}

public abstract class FabricaAbstrata {

  public abstract ConectorAbstrato criarConector();

  public static FabricaAbstrata criarFabricaConcreta() throws Exception {
    Configuracao configuracao = new Configuracao();
    if (configuracao.getBancoDeDados().equals(Configuracao.ORACLE)) {
    	return new FabricaConcretaOracle();
    } else if (configuracao.getBancoDeDados().equals(Configuracao.SQL_SERVER)) {
    	return new FabricaConcretaSQLServer();
    } else {
    	throw new Exception("Configuração de banco de dados não definida");
    }
  }

}

public class FabricaConcretaOracle extends FabricaAbstrata {

  public ConectorAbstrato criarConector() {
    return new ConectorOracle();
  }

}

public class FabricaConcretaSQLServer extends FabricaAbstrata {

  public ConectorAbstrato criarConector() {
    return new ConectorSQLServer();
  }

}

public class Cliente {

  public static void main(String[] args) {
    FabricaAbstrata.criarFabricaConcreta().criarConector().conectar();
  }

}

Listagem 2 – Abstract Factory

Os elementos participantes dessa solução, são:

FabricaAbstrata (no modelo formal, FabricaAbstrata)

É uma classe abstrata. Ela declara o método abstrato criarConector(). Esse método é responsável por retornar um objeto do tipo ConectorAbstrato, cujo o papel é realizar uma conexão com um banco de dados. Mas ele não é implementado nessa classe; a implementação fica por conta das classes FabricaConcretaOracle e FabricaConcretaSQLServer. Nessa classe está declarado também o método estático criarFabricaConcreta(). Seu trabalho é verificar em um arquivo de configuração qual o banco de dados ativo no aplicativo. Se for o Oracle, irá criar e retornar uma instância de FabricaConcretaOracle; se for o SQLServer, irá criar e retornar uma instância de FabricaConcretaSQLServer.

FabricaConcretaOracle (no modelo formal, FabricaConcreta)

Aqui é implementado o método criarConector(). O método cria um objeto do tipo ConectorOracle e retorna esse objeto como do tipo interface ConectorAbstrato.

FabricaConcretaSQLServer (no modelo formal, FabricaConcreta)

Aqui é implementado o método criarConector(). O método cria um objeto do tipo ConectorSQLServer e retorna esse objeto como do tipo interface ConectorAbstrato.

ConectorAbstrato (no modelo formal, ProdutoAbstrato)

É uma interface e declara o método conectar(). Ele é implementado pelas classes ConectorOracle e ConectorSQLServer.

ConectorOracle (no modelo formal, ProdutoConcreto)

É uma classe. Implementa a interface ConectorAbstrato e implementa o seu método conectar(). A conexão com o banco de dados Oracle é realizada por esse método.

ConectorSQLServer (no modelo formal, ProdutoConcreto)

É uma classe. Implementa a interface ConectorAbstrato e implementa o seu método conectar(). A conexão com o banco de dados SQLServer é realizada por esse método.

Cliente

Utiliza a infraestrutura de conexão com banco de dados projetada utilizando o padrão de projeto Abstract Factory. Um exemplo de utilização é:

FabricaAbstrata.criarFabricaConcreta().criarConector().conectar();

Listagem 3 – Abstract Factory

Considerações Finais

Os elementos envolvidos nesse padrão estão interligados de várias formas. Talvez em uma primeira leitura não seja possível compreender esse padrão em toda sua plenitude. O exemplo de aplicação apresentado nesse post é apenas uma forma de aplicação dentro de infinitas. E por último, padrões de projeto, criatividade e prática andam juntos.

Um grande abraço!

Por VICTOR FERRER PETRELLI

Postado em: 25 de fevereiro de 2015

Confira outros artigos do nosso blog

REST não é JSON

21 de agosto de 2017

Bruno Sofiato

[Webinar] Profile de aplicações Java com Oracle Mission Control e Flight Recorder

24 de julho de 2017

Danival Calegari

Criando Mocks de serviços REST com SoapUI

27 de junho de 2017

Monise Costa

JavaScript 6: diferença entre var, let e const

09 de maio de 2017

Otávio Felipe do Prado

Deixe seu comentário