Design Pattern Factory - Parte 1

Factory Method

O design pattern Factory Method envolve a criação de um método que constrói o objeto de uma classe. Ele é usado no lugar do new.

Considere o código a seguir que pode desenhar um caranguejo (Crab) ou um peixe (Fish) dentro de um tanque.


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

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

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

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

}

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

Aquatic cb = new Crab();
drawFishTank(cb);
Aquatic fs = new Fish();
drawFishTank(fs);


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

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

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

Aplicando o Factory Method

Veja como o código é modificado de tal maneira que um método (createAquatic()) passa a ser responsável pela criação dos seres aquáticos.

Então, em vez de usar new e ter que referenciar a classe, você dispara o método criador:

Aquatic cb = ccb.createAquatic();
Aquatic fs = cfs.createAquatic();

Esse é o primeiro passo para isolar o processo de criação do cliente, ou seja, o cliente não toma conhecimento de como (em qual classe) o objeto é criado. Desse modo, quem fabrica terá a liberdade de mudar a implementação como desejar sem afetar o cliente.


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

public interface AquaticCreator {
   public Aquatic createAquatic();
}

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

public class CrabCreator implements AquaticCreator {
   public Aquatic createAquatic() {
        return new Crab();
    }
}

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

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

}

public class FishCreator implements AquaticCreator {
   public Aquatic createAquatic() {
      return new Fish();
  }
}

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

AquaticCreator ccb = new CrabCreator();
AquaticCreator cfs = new FishCreator();

Aquatic cb = ccb.createAquatic();
drawFishTank(cb);
Aquatic fs = cfs.createAquatic();
drawFishTank(fs);


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

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

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

Limitações do Pattern

Mas o método isoladamente é limitado. Você ainda precisa criar o objeto que implementa o método fábrica, o que transfere o new para o criador. Mas logo veremos que este método se tornará parte de um pattern mais poderoso.

Exercício

Retomando o exemplo do empréstimo usado em aulas anteriores:

Considere o código de dois tipos de empréstimo a seguir, o simples e o composto. Modifique a implementação deles, de modo que passem a ser referenciados por uma interface comum e que sejam instanciados por métodos fábrica.


In [4]:
import java.lang.Math;

class EmprestimoSimples {
    private float s;
    private int   n;
    private float j;
    private int   corrente;
    private float p,
                  proxima;

    public EmprestimoSimples(float s, int n, float j) {
        this.s = s;
        this.n = n;
        this.j = j;
        corrente = 1;
        this.p = -1;  // antes da primeira parcela
        this.proxima = s;
    }

    float getS() {
        return s;
    }

    int getN() {
        return n;
    }
    
    float getJ() {
        return j;
    }

    public float proximaParcela() {
        if (corrente <= n)
            p = s + ((corrente-1) * s * (j/100));
        else
            p = 0;
        corrente++;
        return p;
    }
    
    public float parcela() {
        return p;
    }
    
    public float parcela(int numero) {
        float resultado = 0;
        if (numero <= n)
            resultado = s + ((numero-1) * s * (j/100));
        return resultado;
    }
}


class EmprestimoComposto {
    private float s;
    private int   n;
    private float j;
    private int   corrente;
    private float p,
                  proxima;

    public EmprestimoComposto(float s, int n, float j) {
        this.s = s;
        this.n = n;
        this.j = j;
        corrente = 1;
        this.p = -1;  // antes da primeira parcela
        this.proxima = s;
    }

    float getS() {
        return s;
    }

    int getN() {
        return n;
    }
    
    float getJ() {
        return j;
    }

    public float proximaParcela() {
        p = proxima;
        corrente++;
        if (corrente <= n)
            proxima += (proxima * (j/100));
        else
            proxima = 0;
        return p;
    }
    
    public float parcela() {
        return p;
    }
    
    public float parcela(int numero) {
        float resultado = 0;
        if (numero <= n)
            resultado = s * (float)Math.pow(1 + j/100, numero-1);
        return resultado;
    }
}

// codigo principal

EmprestimoSimples emprestimo1 = new EmprestimoSimples(500, 7, 2);
EmprestimoComposto emprestimo2 = new EmprestimoComposto(500, 7, 2);

int i = 1;
emprestimo1.proximaParcela();
emprestimo2.proximaParcela();
while (emprestimo1.parcela() > 0 || emprestimo2.parcela() > 0) {
    if (emprestimo1.parcela() > 0) {
        System.out.println("Emprestimo 1: parcela " + i + " eh " + emprestimo1.parcela());
        System.out.println("              parcela " + i + " eh " + emprestimo1.parcela(i));
    }
    if (emprestimo2.parcela() > 0) {
        System.out.println("Emprestimo 2: parcela " + i + " eh " + emprestimo2.parcela());
        System.out.println("              parcela " + i + " eh " + emprestimo2.parcela(i));
    }
    emprestimo1.proximaParcela();
    emprestimo2.proximaParcela();
    i++;
}


Emprestimo 1: parcela 1 eh 500.0
              parcela 1 eh 500.0
Emprestimo 2: parcela 1 eh 500.0
              parcela 1 eh 500.0
Emprestimo 1: parcela 2 eh 510.0
              parcela 2 eh 510.0
Emprestimo 2: parcela 2 eh 510.0
              parcela 2 eh 510.0
Emprestimo 1: parcela 3 eh 520.0
              parcela 3 eh 520.0
Emprestimo 2: parcela 3 eh 520.2
              parcela 3 eh 520.19995
Emprestimo 1: parcela 4 eh 530.0
              parcela 4 eh 530.0
Emprestimo 2: parcela 4 eh 530.604
              parcela 4 eh 530.60394
Emprestimo 1: parcela 5 eh 540.0
              parcela 5 eh 540.0
Emprestimo 2: parcela 5 eh 541.21606
              parcela 5 eh 541.216
Emprestimo 1: parcela 6 eh 550.0
              parcela 6 eh 550.0
Emprestimo 2: parcela 6 eh 552.0404
              parcela 6 eh 552.04034
Emprestimo 1: parcela 7 eh 560.0
              parcela 7 eh 560.0
Emprestimo 2: parcela 7 eh 563.08124
              parcela 7 eh 563.0811

In [ ]: