Dúvida sobre Abstract Factory

Estou estudando os padrões de projeto Factory Method e Abstract Factory.

O Factory Method ficou bem claro que através de apenas 1 método você passa o parâmetro e ele cria o objeto de acordo com aquele parâmetro. Porém o Abstract Factory pelo que percebi é uma Fabrica de criar Fabricas, e ainda não consegui ver a utilidade disso, mesmo porque o Factory Method o faz de forma mais simples.

O AbstractFactory é um objeto que contém um conjunto de fábricas de coisas relacionadas. Por exemplo, você tem um Game de pacman, e quer permitir a inclusão de skins nesse game. Você pode criar uma AbstractFactory assim:

public abstract class SceneFactory { public abstract PacmanView createPacmanView(); public abstract GhostView createGhostView(); public abstract FruitView createFruitView(); public abstract PillView createPillView(); public abstract WallView createWallView(); }

Se aguém quiser criar uma nova skin para seu game, basta agora implementar essa Factory abstrata. Por exemplo, você poderia querer ter um Pacman com o estilo do gótico:

Veja que para o programador de GothFactory, fica fácil saber todas as fábricas que precisam ser criadas, pois o AbstractFactory contém uma série de TemplateMethods, que são também FactoryMethods.

Então pelo que eu entendi o Abstract Factory serve apenas para definir uma Interface (contrato padrão) do que deve ser implementado, mas esse não é conceito de Classe Abstrata ou mesmo Interface ? Em que o Abstract Factory acrescenta nestes conceitos ?

Estou “voando” um pouco ainda no conceito de Abstract Factory.

[quote=rlanhellas]Então pelo que eu entendi o Abstract Factory serve apenas para definir uma Interface (contrato padrão) do que deve ser implementado, mas esse não é conceito de Classe Abstrata ou mesmo Interface ? Em que o Abstract Factory acrescenta nestes conceitos ?

[/quote]

Não acrescenta nada aos conceitos, ela usa esses conceitos.
Não viaje muito. Muitos padrões são apenas dicas de como aplicar os conceitos para um propósito específico.

Nesse caso, o propósito é facilitar a definição de um conjunto de fábricas de objetos relacionados.

Outros padrões, como o Strategy, são simplesmente a implementação do conceito de polimorfismo. O padrão só ajuda a entender um propósito. No caso do Strategy, o de lembrar que uma classe pode se referir a um algoritmo ou a uma técnica, não somente a objetos do mundo real.

Pode me dar um exemplo do Abstract Factory mais simples ? Essa do Game ficou ainda um pouco “obscuro”

Você pode querer, por exemplo, que seu server possa escolher um entre vários algoritmos de criptografia.

Como vc sabe, é necessário ter uma classe que codifica a mensagem e outra que decodifica.

Você poderia ter uma AbstractFactory assim:

public abstract class CriptoProvider { public abstract Encoder createEncoder(); public abstract Decoder createDecoder(); }

E aí teria as implementações:

Bom, no código abaixo vou tentar demonstrar o uso do Abstract Factory junto com o Factory Method. Me diga se esta correto:

 //Aqui aplico o abstract factory
 public abstract class Carro{
    public abstract Carro criarCarro();
    public abstract void pintarCarro(String cor);
 }

 public class Honda extends Carro{
    
    private String cor;
   
    public Carro criarCarro(){
             return new Honda();
      }

   public void pintarCarro(String cor){
      this.cor = cor;      
  }
  
  public void acelerar(){
     while(pisandoAceleador){
          acelerar();
     }
   }

}

Legal, defini acima uma classe abstrata Carro e uma classe concreta Honda. Posso chamar a implementação acima de Abstract Factory ?

[quote=rlanhellas]Bom, no código abaixo vou tentar demonstrar o uso do Abstract Factory junto com o Factory Method. Me diga se esta correto:

 //Aqui aplico o abstract factory
 public abstract class Carro{
    public abstract Carro criarCarro();
    public abstract void pintarCarro(String cor);
 }

 public class Honda extends Carro{
    
    private String cor;
   
    public Carro criarCarro(){
             return new Honda();
      }

   public void pintarCarro(String cor){
      this.cor = cor;      
  }
  
  public void acelerar(){
     while(pisandoAceleador){
          acelerar();
     }
   }

}

Legal, defini acima uma classe abstrata Carro e uma classe concreta Honda. Posso chamar a implementação acima de Abstract Factory ?[/quote]

Não. Esse padrão é ainda é o Factory Method. A diferença entre estes dois padrões é que no caso do Factory Method, o cliente da fábrica é a própria classe abstrata, por exemplo:

abstract class Service<T>{
  pubic final void save(T obj){
     getDao().save(obj);
  }

  protected abstract DAO<T> getDao();
}

ou seja, a superclasse delega para uma classe filha a criação dos objetos que ela precisa;

No caso da AbstractFactory, além da possibilidade de servir de fábrica para vários tipos de objetos, ela é baseada em composição. O cliente da AbstractFactory não é uma superclasse de AbstractFactory, mas uma outra classe qualquer, por exemplo:

class WidgetFactory{
  getButton();
  getTextInput();
  getListBox();
}

class FormularioCliente extends Formulario{
  
  FormularioCliente(WidgetFactory factory){
      Button bt = factory.getButton();
      bt.setText("Salvar");
      add(bt);
  }
}

No caso o Factory Method é utilizado em conjunto com o Abstract Method correto ?

Por exemplo:

 abstract class WidgetFactory
 {
     public static WidgetFactory obterFactory()
     {
 
         if( Configuracao.obterInterfaceGraficaAtual() == Configuracao.MotifWidget )
         {
             return new MotifWidgetFactory();
         }
         else
         {
             return new QtWidgetFactory();
         }
    }
 
    public abstract Botao criarBotao();
 }
 
 class MotifWidgetFactory extends WidgetFactory
 {
     public Botao criarBotao()  {
         return new BotaoMotif();
     }
 }
 
 class QtWidgetFactory extends WidgetFactory
 {
     public Botao criarBotao()  {
         return new BotaoQt();
     }
  }
 
 abstract class Botao
 {
     public abstract void desenhar();
 }
 
 class BotaoMotif extends Botao
 {
     public void desenhar()  {
        System.out.println("Eu sou um botao Motif!");
     }
 }
 
 class BotaoQt extends Botao
 {
     public void desenhar()  {
        System.out.println("Eu sou um botao Qt!");
     }
 }
 
 public class Cliente
 {
     public static void main(String[] args)
     {
         WidgetFactory factory = WidgetFactory.obterFactory();
 
         Botao botao = factory.criarBotao();
         botao.desenhar();
     }
 }

No exemplo acima ocorre o Factory Method quando fazemos

 if( Configuracao.obterInterfaceGraficaAtual() == Configuracao.MotifWidget )
         {
             return new MotifWidgetFactory();
         }
         else
         {
             return new QtWidgetFactory();
         }

O conjunto todo é o Abstract Factory visto que a fabrica abstrata WidgetFactory diz o que as suas fabricas concretas devem implementar, ou seja, a função do abstract factory é dizer para suas fabricas filhas o que deve ser implementado mas não como deve ser implementado. Assim sendo quando o usuário for instanciar um MotifWidgetFactory ele não chama diretamente o MotifWidgetFactory, mas chama o WidgetFactory que criará um MotifWidgetFactory. Certo ? Isso ae é uma mistura de Factory Method com Abstract Factory.

[quote=rlanhellas]No caso o Factory Method é utilizado em conjunto com o Abstract Method correto ?

Por exemplo:

 abstract class WidgetFactory
 {
     public static WidgetFactory obterFactory()
     {
 
         if( Configuracao.obterInterfaceGraficaAtual() == Configuracao.MotifWidget )
         {
             return new MotifWidgetFactory();
         }
         else
         {
             return new QtWidgetFactory();
         }
    }
 
    public abstract Botao criarBotao();
 }
 
 class MotifWidgetFactory extends WidgetFactory
 {
     public Botao criarBotao()  {
         return new BotaoMotif();
     }
 }
 
 class QtWidgetFactory extends WidgetFactory
 {
     public Botao criarBotao()  {
         return new BotaoQt();
     }
  }
 
 abstract class Botao
 {
     public abstract void desenhar();
 }
 
 class BotaoMotif extends Botao
 {
     public void desenhar()  {
        System.out.println("Eu sou um botao Motif!");
     }
 }
 
 class BotaoQt extends Botao
 {
     public void desenhar()  {
        System.out.println("Eu sou um botao Qt!");
     }
 }
 
 public class Cliente
 {
     public static void main(String[] args)
     {
         WidgetFactory factory = WidgetFactory.obterFactory();
 
         Botao botao = factory.criarBotao();
         botao.desenhar();
     }
 }

No exemplo acima ocorre o Factory Method quando fazemos

 if( Configuracao.obterInterfaceGraficaAtual() == Configuracao.MotifWidget )
         {
             return new MotifWidgetFactory();
         }
         else
         {
             return new QtWidgetFactory();
         }

O conjunto todo é o Abstract Factory visto que a fabrica abstrata WidgetFactory diz o que as suas fabricas concretas devem implementar, ou seja, a função do abstract factory é dizer para suas fabricas filhas o que deve ser implementado mas não como deve ser implementado. Assim sendo quando o usuário for instanciar um MotifWidgetFactory ele não chama diretamente o MotifWidgetFactory, mas chama o WidgetFactory que criará um MotifWidgetFactory. Certo ? Isso ae é uma mistura de Factory Method com Abstract Factory.[/quote]

Quase isso … não existe nada que proíba você combinar os padrões, por outro lado, também não tem nada que o obrigue. Como eu disse, são dois padrões diferentes que são usados em momentos diferentes;

Uma implementação de AbstractFactory não caracteriza os métodos da AbstractFactory como FactoryMethod’s. Isso porque a AbstractFactory não vai usar os objetos criados por suas implementações, elas serão usadas por classes que são clientes da AbstractFactory. O caso do Factory Method é diferente, a classe que declara o Factory Method é a mesma classe que vai usá-lo, porém, ela apenas delega a criação para uma subclasse.

Reabrindo o tópico, surgiu um exemplo legal para tentar aplicar o Abstract Factory e gostaria que vocês me ajudassem:

Tenho uma Classe Pessoa que se especializa em PessoaFisica e PessoaJuridica. A PessoaFisica ainda pode se especializar em Funcionario ou Dentista. Como ficaria isso aplicando Abstract Factory e Factory Method ?.

Sendo que eu quero chamar apenas 1 fabrica capaz de criar dentista, funcionario ou pessoaJuridica conforme o parametro passado.

[quote=rlanhellas]Reabrindo o tópico, surgiu um exemplo legal para tentar aplicar o Abstract Factory e gostaria que vocês me ajudassem:

Tenho uma Classe Pessoa que se especializa em PessoaFisica e PessoaJuridica. A PessoaFisica ainda pode se especializar em Funcionario ou Dentista. Como ficaria isso aplicando Abstract Factory e Factory Method ?.

Sendo que eu quero chamar apenas 1 fabrica capaz de criar dentista, funcionario ou pessoaJuridica conforme o parametro passado.[/quote]

e a pergunta de 1 milhão de reaishmmm …

A classe que é o cliente da fábrica trabalha apenas com o tipo mais abstrato ?

Outra dica: já vi 4 níveis de profundidade na sua hierarquia de classes, será que não cabe uma composição ai não ? Será que ao invés de dizer que Funcionario É-UM Dentista não ficaria melhor dizer que Funcionario TEM-UMA ProfissaoDentista ?

A classe que é cliente da fábrica (nesse caso um JFrame) pode utilizar tanto PessoaJuridica, Dentista ou Funcionario.

rmendes08, acontece que o dentista possui alguns campos que o funcionário não tem como: CRO, Porcentagem de Ganho, Formação (Universidade) e etc…

Tudo bem, mas a minha pergunta é: o tipo da variável usada é sempre o tipo mais abstrato, Pessoa ? Porque não adianta nada você abstrair a criação do objeto para depois usar instanceof em cima da variável.

Verdade, nem sempre vou usar apenas Pessoa, na verdade são raras as vezes que vou usar Pessoa, vou utilizar mais : PessoaJuridica, Dentista ou Funcionario.

Mas não deixarei a classe PessoaFisica e Pessoa como abstrata pois poderemos utilizar ela em alguma parte da aplicação.

Tudo bem, a princípio, tanto herança quanto composição resolvem o problema de adicionar campos que não são comuns a qualquer funcionário. O problema é que ao utilizar herança você aumenta o acoplamento entre objetos. Um Funcionario, que é criado como Dentista por exemplo, nunca poderá ser outra coisa.

Agora, falando em termos mais objetivos. Se você estiver montando um cadastro, utlizar muitos níveis de herança pode dificultar o seu projeto. Por exemplo, sempre que você tiver que cadastrar um Dentista, sua tela precisará de todos os campos de Pessoa, PessoaFisica e Funcionario; E isso pode ser terrível em termos de UX (User eXperience). Por outro lado, se você aplica composição você pode criar uma tela apenas para cadastro de Pessoa, uma tela para o cadastro de Funcionario e outra para o cadastro de Dentista, bastando apenas que você faça a ligação entre as entidades e compondo os objetos necessários.

não não, quando você diz: “sempre que você tiver que cadastrar um Dentista, sua tela precisará de todos os campos de Pessoa, PessoaFisica e Funcionario”.

Dentista não herda de funcionario, ele herda de PessoaFisica.