[Resolvido]Factory e abstract factory

Então pessoal sou novo no fórum e resolvi pedir ajuda quando a implementação dos patterns Factory e Abstract Factory.
Eu fiz um projeto bem simples só para testar, se é que isso pode ser considerado testar, o banco de dados H2. Ate ai tudo bem, mas eu resolvi usar os dois designs nesse meu pequeno teste e fiquei com duvida se fiz certo ou não, gostaria que vocês me dissessem se está correto ou não, e o que teria que ser arrumado.

Então o meu projeto tem um DAO para acesso com o banco H2, minha abstract factory seria, então, a responsavel por criar a factory dos DAOs do banco h2 e outros, no caso a Interface FabricaDAO ?

public class Fabrica {
	
	public enum Tipo{
		H2{

			@Override
			public FabricaDAO createFabricaDAO() {
				return new H2fabrica();
			}};
		
		public abstract FabricaDAO createFabricaDAO();
	};

	public static FabricaDAO createFabricaDAO(Tipo tipo){
		return tipo.createFabricaDAO();
	}
	
}

A factory que ele devolve é a responsavel por criar os DAO, nesse caso ele so retornara uma fabrica para os DAOs do H2.

public class H2fabrica implements FabricaDAO {

	@Override
	public UsuarioDAO createUsuarioDAO() {
		return new H2UsuarioDao();
	}

}

A interface que ele implementa é:

public interface FabricaDAO {

	public UsuarioDAO createUsuarioDAO();
	
}

Bem eu disse que era algo bem banal…
Ele retorna o UsuarioDAO que acessa o banco H2, a classe dele é esta:

public class H2UsuarioDao implements UsuarioDAO {

	Connection connection;
	
	@Override
	public boolean salvar(Usuario usuario) throws SQLException {
		//Implementação aqui...
	}

	@Override
	public boolean deletar(Usuario usuario) throws SQLException {
		//Implementação aqui...
	}

	@Override
	public boolean deletarPorId(long id) throws SQLException {
		//Implementação aqui...
	}

	@Override
	public boolean alterar(Usuario usuario) throws SQLException {
		//Implementação aqui...
	}

	@Override
	public List<Usuario> listar() throws SQLException {
		//Implementação aqui...
	}

	@Override
	public Usuario buscarPorId(long id) throws SQLException {
		//Implementação aqui...
	}

	@Override
	public List<Usuario> buscarPorNome(String nome) throws SQLException {
		//Implementação aqui...
	}

}

E a interface do UsuarioDAO é esta:

public interface UsuarioDAO {
	
	public boolean salvar(Usuario usuario) throws SQLException;
	
	public boolean deletar(Usuario usuario) throws SQLException;
	public boolean deletarPorId(long id) throws SQLException;
	
	public boolean alterar(Usuario usuario) throws SQLException;
	
	public List<Usuario> listar() throws SQLException;
	
	public Usuario buscarPorId(long id) throws SQLException;
	public List<Usuario> buscarPorNome(String nome) throws SQLException;
	
}

E apenas como exemplo para usar isso eu faria algo como

public static void main(String[] args) {
		FabricaDAO fabricaDAO = Fabrica.createFabricaDAO(Tipo.H2);
		UsuarioDAO usuarioDAO = fabricaDAO.createUsuarioDAO();
                //Utilizar o usuarioDAO aqui...
}

Então essa implementação que eu fiz dos designs esta correta ou eu apenas fiz uma bagunça ?
Se estivar errada poderiam por favor dizer onde eu errei e como consertar.
Como eu sou novo no JAVA gostaria de pedir indicações de alguns artigos sobre o assunto para ler, caso conheçam.
PS: Desculpem por qualquer erro de grafia, eu tenho a mania de não suar acentos.

Vamos pensar em conjunto sobre a sua solução…

Definição de Abstract Factory:

Provide an interface for creating families of related or dependent objects without specifying their concrete classes [1].

Em pt-BR:

Provê uma interface para criar familias de objetos dependentes ou relacionados sem especificar a classe concreta.

Atenção nessa parte: sem especificar a classe concreta

No seu código você acha que está especificando a classe concreta, ou não?

Eu creio que na parte da abstract factory eu não especifiquei a classe concreta, apenas uma interface que seria a FabricaDAO, correto ?

Correto, você não especificou a classe concreta. Mas nessa linha…

FabricaDAO fabricaDAO = Fabrica.createFabricaDAO(Tipo.H2);  

Você forçou o tipo ser H2, logo a classe da FabricaDAO é esperada que seja H2fabrica, correto?

Isso seria o mesmo que fazer:

FabricaDAO fabricaDAO = new H2Fabrica();

Concorda?

Se concordar, você está especificando o tipo concreto da classe de fábrica. Nesse caso, você acha que ainda tem um Abstract Factory de acordo com a definição acima?

Sim, mas a classe Fabrica no caso seria apenas uma factory correto ?

A abstract factory não seria a interface FabricaDAO ?
Que depois seria usada sem especificar a classe concreta como em

UsuarioDAO usuarioDAO = fabricaDAO.createUsuarioDAO();

Exato. Você está correto.

Eu chamei a atenção para essa Fabrica no seu desenho porque ela não faz parte do design abstract factory.

Preenchendo as lacunas da definição teriamos:

Provê uma interface (FabricaDAO) para criar familias de objetos dependentes ou relacionados (UsuarioDAO, FuncionarioDAO, etc) sem especificar a classe concreta (H2UsuarioDAO, MySQLUsuarioDAO, H2FuncionarioDAO, MySQLFuncionarioDAO).

Resolvido o raciocínio do AbstractFactory.

Agora, voltando na questão da classe Fabrica… qual o objetivo dela?

Seria uma fabrica, design factory, para os tipos de persistencia, por exemplo: a minha ussa o banco h2, mas caso seja necessario trocar para o MySql por exemplo seria uma alteração mais simples por não estar hard-coded.
Pelo menos foi isso que eu pensei na hora, so não tenho muita certeza agora que você chamou a atenção e eu reli as classes.

Por exemplo se eu não tivesse ela teria que instanciar a FabricaDAO assim

    FabricaDAO fabricaDAO = new H2Fabrica(); 

So fosse uma alteração muito grande desse modo acima não seria pior ?

De acordo com o GOF, que é o grupo que criou o catálogo tradicional de design patterns, existem 2 patterns que tem Factory no nome:

Abstract Factory (que é implementado no seu código pelo FactoryDAO)
e
Factory Method (que não é implementado no seu código)

Essa classe Fabrica não implementa nenhum dos dois.

No entanto, para efeitos didáticos vamos supor um design pattern Factory. Que é uma classe com um método estático que retorna um objeto que não sabemos qual é, semelhante a forma como implementou na classe Fabrica. Ok?

Esse código:

FabricaDAO fabricaDAO = Fabrica.createFabricaDAO(Tipo.H2); 

Oferece alguma vantagem em relação a esse:

FabricaDAO fabricaDAO = new H2Fabrica(); ?

E ainda, não seria interessante se na utilização da classe Fabrica o código fosse assim:

FabricaDAO fabricaDAO = Fabrica.createFabricaDAO(); ?

Poderia ser, para isso teria apenas que mudar o metodo e ficaria assim:

public class Fabrica {
	
	public static FabricaDAO createFabricaDAO(){
		return new H2fabrica();
	}
	
}

Mas desse mode eu teria que criar um metodo novo para cada classe que implementasse minha FabricaDAO ?
Por exemplo, e se eu quisesse a possibilidade de retornar tanto a H2Fabrica, como uma outra, a TXTFabrica para persistir os dados em um arquivo txt ?

Outra pergunta a minha classe H2fabrica não corresponde ao pattern Factory ?

A questão que não está resolvida no Design Pattern Abstract Factory é: Qual ConcreteFactory criar?

E isso pode ser feito de várias formas, por exemplo:

FabricaDAO fabricaDAO = Fabrica.createFabricaDAO(Tipo.H2);  
FabricaDAO fabricaDAO = new H2Factory();

Porém, essas duas formas são praticamente equivalentes. A única diferença é que no primeiro exemplo você criou um nível a mais para chegar no new H2Factory() no segundo exemplo isso é feito diretamente. Arquiteturalmente falando, poderiamos dizer que a vantagem do primeiro exemplo é quase zero em relação ao segundo. Você poderia argumentar que o primeiro exemplo é mais estético, e a estética do código é valida, porém ele requer mais código.

Um terceiro exemplo equivalente poderia ser:

[code]
public class Fabrica {

public static final int H2 = 1;
public static final int TXT = 2;
  
public static FabricaDAO createFabricaDAO(int factoryConst){  
    switch(factoryConst){
        case H2: return new H2Factory();
        case TXT: return new TXTFactory();
    }
    throw new RuntimeException("Factory unknown"); 
}  

} [/code]

E faria assim no main:

FabricaDAO fabricaDAO = Fabrica.createFabricaDAO(Fabrica.H2); 

Quando eu falo que eles são arquiteturalmente equivalentes eu quero dizer que no main você sempre passa a referencia de qual fábrica concreta criar.

Depois você pode pensar quais seriam as vantagens de cada modelo (as vantagens são mais questões de estética, ou de facilidade de codificação, tipos bem definidos do que arquiteturais)

Voltando aqui a criação sem parametros, uma possível implementação seria definir o tipo em um arquivo de configuração:

public class Fabrica {  
      
    public static FabricaDAO createFabricaDAO(){  
        String tipo;
        //leia a configuração e salve na variavel tipo
        if(tipo.equals("H2")){
            return new H2fabrica();  
        }
        if(tipo.equals("TXT")){
            return new TXTfabrica();  
        }
    }  
      
} 

A variável tipo poderia também ser lida de uma variável estática em uma classe de configuração do sistema. Ela deveria ser configurada ao ligar o sistema.

O exemplo foi um pouco rustico mas acho que deu para entender a idéia. Daria para incrementar se no arquivo de configuração você escrever o caminho completo da classe e criar o objeto usando reflection.

Nesse último modelo existem vantagens arquiteturais sobre os anteriores, você poderia citar alguma?

(
Para criar um objeto de uma classe usando o nome dela via String faça:

String className = "pacote.H2Factory"; FactoryDAO f = (FactoryDAO)Class.forName(className).newInstance();)

A unica vantagem que eu vejo agora é que eu não passei um parametro, e nesse caso o sistema esta um pouco mais desaclopado, se esta for a palavra certa.
A fabrica criada poderia ser alterada sem mecher no codigo fonte.

Isso mesmo, você poderia trocar a configuração do sistema sem ter que recompilar.

Se você colocar na configuração o nome completo da classe, ainda tem uma segunda vantagem.
A classe Fabrica seria desacoplada de todas as classes concretas.
Permitindo que novas fábricas concretas sejam adicionadas ao sistema, sem a necessidade de recompilar.

Por exemplo, você poderia criar uma FabricaX e distribuir em um arquivo JAR.
Apenas colocando esse JAR na aplicação e alterando o arquivo de configuração você teria uma nova funcionalidade no sistema.

Toda essa discução foi para incentivar a critica a arquitetura. O importante não é apenas usar o design pattern mas saber porque está usando.

Com o tempo você acabará criando arquiteturas robustas que possivelmente usem algum design pattern mas sem nem saber que está usando um. O segredo é criar soluções para os problemas e analisar os prós e contras da solução. Existem dois principios básicos que são mais importantes de seguir do que exatamente implementar um design pattern específico:

  • Favor composition over inheritance
  • Program to an interface, not an implementation

Mas pelo visto você está bem seguro das suas soluções, está de parabéns.

Espero ter ajudado. Até mais

Obrigado pela ajuda rogelgarcia, realmente as perguntas que você fez me fez para e pensar um poco, mas acho que agora eu entendo o conceito do abstract factory.

Apenas uma ultima pergunta: no caso da minha concrete Factory a H2Fabrica, o metodo dela createUsuarioDAO() é um factory method ?

Não, factory method é um pouco diferente disso tudo.

Vou tentar explicar.

Suponha que exista uma aplicação que tenha um FileManager que abra e salve arquivos. E que essa aplicação trabalhe com vários tipos de arquivo. A operação de abrir e salvar sao iguais para todos, mas cada arquivo tem que ser criado de uma forma diferente.

O tipo de arquivo eu chamo de Document.

Eu teria vários FileManager com a funcionalidade de ler e salvar já definidas na classe abstrata, e deixaria para os FileManager específicos a criação do documento específico.

Exemplo:

abstract class FileManager {
    public void save(File file){
          Document doc = createDocument();
          IO.save(file, doc.getBytes());
    }
    public void open(File file){
          Document doc = createDocument();
          doc.setBytes(IO.loadBytes(file));
    }
    abstract Document createDocument();
}

class WordFileManager extends FileManager {
    public Document createDocument(){
        return new WordDocument();
    }
}
class ExcelFileManager extends FileManager {
    public Document createDocument(){
        return new ExcelDocument();
    }
}

Ficou meio rascunhada a explicação e o exemplo… Se tiver alguma dúvida é só perguntar

O createDocument() é um Factory Method

É… até que podemos pensar no método createUsuarioDAO como um factory method, por ser um método que cria um objeto. MAS a intenção do Factory Method definido pelo GOF é diferente do createUsuarioDAO. Então… há controvérsias para essa afirmação…

Então no caso eu passo a responsabilidade da criação para minhas sub-classes, as classes que herdam o metodo abstract.
E eu estou apenas criando um objeto e não uma “serie” deles.

Eu tive a duvida porque o metodo createUsuarioDAO() sofre quase o mesmo processo, ele é declarado em uma interface e sua classe concreta e quem se preucupa com criação.
Então seria certo falar que minha classe concreta H2fabrica cria os objetos atrevas de um factory method ?
Ou a herança é necessaria para definir um factory method ?

[quote]Então no caso eu passo a responsabilidade da criação para minhas sub-classes, as classes que herdam o metodo abstract.
E eu estou apenas criando um objeto e não uma “serie” deles.[/quote]

Isso.

[quote]Eu tive a duvida porque o metodo createUsuarioDAO() sofre quase o mesmo processo, ele é declarado em uma interface e sua classe concreta e quem se preucupa com criação.
Então seria certo falar que minha classe concreta H2fabrica cria os objetos atrevas de um factory method ?
Ou a herança é necessaria para definir um factory method ?[/quote]

De acordo com o GOF seria necessário que a classe superior seja abstrata e tenha código. Apenas o método de criação específica seria deixado para as sub-classes.

Vou dar outro exemplo:

abstract class GenericDAO {
    abstract Class getBeanClass();

    public List list(){
         Class beanClass = getBeanClass();
         return findListForClass(beanClass);
    }

    public List findListForClass(Class x){
          ....
    }
}

class UsuarioDAO extends GenericDAO{
    public Class getBeanClass(){
         return Usuario.class;
    }
}

class FuncionarioDAO extends GenericDAO{
    public Class getBeanClass(){
         return Funcionario.class;
    }
}

O método getBeanClass é um Factory Method

Agora eu consegui entender, realmente muito obrigado pela sua paciencia em me ajudar.
Me explicou um punhado de coisa de forma bem simples e coesa.

Obrigado!