Construtores são necessários, obviamente, mas eles tem um pequeno problema: não tem nome.
Considere uma classe Dado e um objeto instanciado a partir dela. Sabendo que cada Dado tem número de lados determinado, quantos lados tem este dado:
Dado dado = new Dado(); dado.joga(); System.out.println(dado);
Digamos que a implementação de Dado preveja como padrão 6 lados e dê a opção de construir um Dado com um número arbitrário de lados (sem validações para brevidade):
package aps.livro;
import static java.lang.Math.random;
public class Dado {
private int numero;
private final int lados;
public Dado() {
this(6); // cria um dado de seis lados por padrão
}
public Dado(int lados) {
this.lados = lados;
}
public int getNumero() { return numero; }
public int getLados() { return lados; }
public void joga() { numero = (int) (random() * lados + 1); }
@Override
public String toString() { return String.valueOf(numero); }
}
Mesmo que instanciemos o Dado passando lados, quem lê apenas a construção e não tem acesso ao código-fonte não pode afirmar com certeza o significado do parâmetro, veja a seguir:
Dado dado = new Dado(10); dado.joga(); System.out.println(dado);
É intuitivo pensar que Dado(10) é um Dado com 10 lados, mas não é algo implícito.
Embora eu programe com mais frequência em Java, também estou atento e programo em outras linguagens, que me faz melhor como programador Java. Como assim? Bem, aprender diferentes culturas de implementação te ajudam a ver os problemas de mais pontos de vista. Um ditado presente no Zen do Python diz:
Explicit is better than implicit
Nesta implementação do Dado, o construtor é implícito, ou seja, nós deduzimos que Dado(10) é um Dado com *dez* lados pois está implícito.
Que tal tornar explícito? Não é complicado, basta adicionar um método fábrica. Veja exemplo:
package aps.livro;
import static java.lang.Math.random;
public class Dado {
private int numero;
private final int lados;
// método para fabricação de Dados
public static Dado lados(int lados) {
return new Dado(lados);
}
public Dado() {
this(6); // cria um dado de seis lados por padrão
}
public Dado(int lados) {
this.lados = lados;
}
public int getNumero() { return numero; }
public int getLados() { return lados; }
public void joga() { numero = (int) (random() * lados + 1); }
@Override
public String toString() { return String.valueOf(numero); }
}
A grande vantagem de um método fábrica é que ele tem nome e com nome ele demonstra a intenção explicitamente. Veja uma situação de uso:
Dado dado = Dado.lados(10); // bem mais claro, certo dado.joga(); System.out.println(dado);
Ainda é possível predefinir construções comuns com métodos sem parâmetros, por exemplo, dados de 6, 10 e 20 lados.
package aps.livro;
import static java.lang.Math.random;
public class Dado {
private int numero;
private final int lados;
public static Dado lados(int lados) {
return new Dado(lados);
}
// métodos de fabricação com parâmetro constante
public static Dado deSeisLados() { return lados(6); }
public static Dado deDezLados() { return lados(10); }
public static Dado deVinteLados() { return lados(20); }
// ... restante omitido
Veja a clareza que fica:
Dado dado = Dado.deDezLados(); // bem mais claro que: new Dado(10) dado.joga(); System.out.println(dado);
Mais detalhes podem ser vistos no livro do Josh Bloch:
http://www.submarino.com.br/produto/6800218/livro-java-efetivo
Vale a pena gasta um minuto para ler o Zen do Python:
http://www.python.org/dev/peps/pep-0020/
O código-fonte dos exemplos está disponível aqui: https://github.com/marciojrtorres/livro-aps/tree/master/static_factory_method
