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