Design Pattern Factory - Parte 2

Abstract Factory

O Abstract Factory é uma generalização do conceito, de forma a isolar completamente o cliente do processo de construção. A ideia básica é que este pattern abstrai as próprias fábricas, permitindo a construção de fábrica de fábricas. Vamos entendê-lo por etapas.

Etapa 1 - Primeiro rascunho do Abstract Factory

Primeiro vamos transformar a interface AquaticCreator em uma interface de fábrica AbstractFactory.

public interface AbstractFactory {
   public Aquatic createAquatic();
}

Desse modo, há uma fábrica concreta para caranguejos e outra para peixes, ambas concretizam a fábrica abstrata. Esta será futuramente estendida para se fabricar outras coisas além de somente os seres aquáticos.

Etapa 2 - Montando uma Fábrica de Fábricas

Para isolar completamente as fábricas de seus clientes, vamos implantar uma fábrica de fábricas, que as cria com um Factory Method. A fábrica de fábricas não faz parte do pattern, mas é uma extensão criada a partir dele.


In [2]:
public interface Aquatic {
    public String aquaticImage();
}

public interface AbstractFactory {
   public Aquatic createAquatic();
}

public class Crab implements Aquatic {
    /*
       o o
       | |  
     /-----\
     |     |
     \-----/
     / / \ \
     */
    
    
    public String aquaticImage()
    {
        return "  o o\n" +
               "  | |\n" +  
               "/-----\\\n" +
               "|     |\n" +
               "\\-----/\n" +
               "/ / \\ \\\n";
    }
}

public class CrabFactory implements AbstractFactory {
   public Aquatic createAquatic() {
        return new Crab();
    }
}

public class Fish implements Aquatic {
    /*   .  _
         |\/O\
         |/\_/
     */

    public String aquaticImage()
    {
        return ".  _\n" +
               "|\\/O\\\n" +
               "|/\\_/\n";
    }

}

public class FishFactory implements AbstractFactory {
   public Aquatic createAquatic() {
      return new Fish();
  }
}

public class GeneralFactory {
    public static AbstractFactory createFactory(String ftype) {
       AbstractFactory f = null;
       if (ftype.equals("crab"))
          f = new CrabFactory();
       else if (ftype.equals("fish"))
          f = new FishFactory();
       return f;
    }
}

public static void drawFishTank(Aquatic theAquatic)
{
    System.out.println("+-----------------+");
    System.out.println("|                 |");
    System.out.println(theAquatic.aquaticImage());       
    System.out.println("|                 |");
    System.out.println("+-----------------+");
}

AbstractFactory fcb = GeneralFactory.createFactory("crab");
AbstractFactory ffs = GeneralFactory.createFactory("fish");

Aquatic cb = fcb.createAquatic();
drawFishTank(cb);
Aquatic fs = ffs.createAquatic();
drawFishTank(fs);


+-----------------+
|                 |
  o o
  | |
/-----\
|     |
\-----/
/ / \ \

|                 |
+-----------------+
+-----------------+
|                 |
.  _
|\/O\
|/\_/

|                 |
+-----------------+

Analisando o código

Note que no programa principal não foi feita nenhuma referência a classe de implementação, apenas às interfaces. Esse é o grande poder das fábricas.

Exercício

Crie uma fábrica de fábricas de empréstimos seguindo a lógica acima.


In [ ]:

Expandindo o Abstract Factory

Vamos imaginar que em vez de um único produto, queremos criar, para cada fábrica, uma linha de produtos compatíveis entre si. Por exemplo, considere que o caranguejo precisa se um aquário específico para ele e que o peixe precisa de outro aquário específico para ele.

Veja a seguir como a fábrica foi estendida para criar dois produtos para cada tipo, compatíveis entre si.

public interface AbstractFactory {
    public Aquatic createAquatic();
    public Aquarium createAquarium();
}

O primeiro método cria um ser aquático e o segundo um aquário para o respectivo ser aquático. O princípio é que a fábrica sempre cria produtos compatíveis entre si. Se é uma fábrica de caranguejos, criará o aquário e o ser aquático compatíveis e o mesmo vale para peixe. Veja o código a seguir.


In [3]:
public interface Aquarium
{
    public String topAquarium();
    public String bottomAquarium();
}

public class FishAquarium implements Aquarium {
    public String topAquarium()     {
        return "+----------------+\n" +
               "|                |";
    }


    public String bottomAquarium()     {
        return "|                |\n" + 
               "+----------------+";
    }
}

public class CrabAquarium implements Aquarium {
    public String topAquarium()     {
        return "/================\\\n" +
               "||              ||";
    }


    public String bottomAquarium() {
        return "||              ||\n" + 
               "\\=================/";
    }
}

public interface AbstractFactory {
    public Aquatic createAquatic();
    public Aquarium createAquarium();
}

public class CrabFactory implements AbstractFactory {
    public Aquatic createAquatic() {
        return new Crab();
    }

    public Aquarium createAquarium() {
        return new CrabAquarium();
    }
}

public class FishFactory implements AbstractFactory {
    public Aquatic createAquatic() {
        return new Fish();
    }

    public Aquarium createAquarium() {
        return new FishAquarium();
    }
}

public class GeneralFactory {
    public static AbstractFactory createFactory(String ftype) {
       AbstractFactory f = null;
       if (ftype.equals("crab"))
          f = new CrabFactory();
       else if (ftype.equals("fish"))
          f = new FishFactory();
       return f;
    }
}

public static void drawFishTank(AbstractFactory factory) {
    Aquatic theAquatic = factory.createAquatic();
    Aquarium theAquarium = factory.createAquarium();

    System.out.println(theAquarium.topAquarium());
    System.out.println(theAquatic.aquaticImage());
    System.out.println(theAquarium.bottomAquarium());
}

AbstractFactory fcb = GeneralFactory.createFactory("crab");
AbstractFactory ffs = GeneralFactory.createFactory("fish");

drawFishTank(fcb);
drawFishTank(ffs);


/================\
||              ||
  o o
  | |
/-----\
|     |
\-----/
/ / \ \

||              ||
\=================/
+----------------+
|                |
.  _
|\/O\
|/\_/

|                |
+----------------+

Analisando o Código

Se você analisar o novo método drawfishTank isoladamente:

public static void drawFishTank(AbstractFactory factory) {
    Aquatic theAquatic = factory.createAquatic();
    Aquarium theAquarium = factory.createAquarium();

    System.out.println(theAquarium.topAquarium());
    System.out.println(theAquatic.aquaticImage());
    System.out.println(theAquarium.bottomAquarium());
}

Verá que ele recebe uma fábrica abstrata, sem saber de quem se trata. Ela funcionará corretamente para qualquer fábrica, mesmo para linhas de produtos novos que ela ainda não conhece. Por exemplo, se no futuro criarmos uma fábrica de anêmonas.

Exercício

Escreva abaixo o que seria uma interface para uma fábrica de seus componentes. Usar fábricas será um critério que valorizará positivamente seu trabalho e que deve ser adotado.


In [ ]: