Classes de fábrica [fechadas]
7 answers
A ideia aqui é a separação de preocupações: se o código que usa o objecto também tem informação suficiente para o instanciar, você não precisa de uma fábrica. No entanto, se houver alguma lógica ou configuração envolvida que você não quer ter o usuário da API para pensar (ou mexer com), você pode esconder tudo isso (e encapsulá-lo para reutilização) em uma fábrica.
Aqui está um exemplo: você quer acessar um dos Serviços fornecidos pelo Google App Engine. O mesmo código deve funcionar em ambos os ambiente de desenvolvimento (do qual existem duas versões, master-slave e high-availabilty) e o ambiente de desenvolvimento local completamente diferente. O Google não quer falar sobre o funcionamento interno de sua infraestrutura interna, e você realmente não quer saber. Então o que eles fazem é fornecer interfaces e fábricas (e várias implementações dessas interfaces para as fábricas escolherem a partir do que você nem precisa saber).
class SamplerFactory
{
private static Hashtable<SamplingType, ISampler> samplers;
static
{
samplers = new Hashtable<SamplingType, ISampler>();
samplers.put(SamplingType.Scalar, new ScalarSampler());
samplers.put(SamplingType.Vector, new VectorSampler());
samplers.put(SamplingType.Array, new ArraySampler());
}
public static ISampler GetSampler(SamplingType samplingType)
{
if (!samplers.containsKey(samplingType))
throw new IllegalArgumentException("Invalid sampling type or sampler not initialized");
return samplers.get(samplingType);
}
}
E aqui está um exemplo de Utilização:
ISampler sampler = SamplerFactory.GetSampler(SamplingType.Array);
dataSet = sampler.Sample(dataSet);
Como vês, não é muito código, e pode até ser mais curto e mais rápido só de fazer
ArraySampler sampler = new ArraySampler();
dataSet = sampler.Sample(dataSet);
Do que usar a fábrica. Então porque me dou ao trabalho? Bem, há duas razões básicas, que se baseiam em cada outros:
-
Primeiro, é a simplicidade e a manutenção do Código. Digamos que no código de chamada, o
enum
é fornecido como um parâmetro. Ou seja, se eu tivesse um método que precisasse de processar os dados, incluindo a amostragem, eu poderia escrever:
Em vez de uma construção mais pesada, como esta:void ProcessData(Object dataSet, SamplingType sampling) { //do something with data ISampler sampler = SamplerFactory.GetSampler(sampling); dataSet= sampler.Sample(dataSet); //do something other with data }
Note que esta monstruosidade deve ser escrita sempre que preciso de amostras. E você pode imaginar como será divertido mudar se, vamos eu adicionei um parâmetro ao construtor, ou adicionei um novo. E esta fábrica tem apenas três opções agora, imagine uma fábrica com 20 implementações.void ProcessData(Object dataSet, SamplingType sampling) { //do something with data ISampler sampler; switch (sampling) { case SamplingType.Scalar: sampler= new ScalarSampler(); break; case SamplingType.Vector: sampler= new VectorSampler(); break; case SamplingType.Array: sampler= new ArraySampler(); break; default: throw new IllegalArgumentException("Invalid sampling type"); } dataSet= sampler.Sample(dataSet); //do something other with data }
-
Segundo, é a dissociação do Código. Quando eu uso uma fábrica, o código de chamada não sabe ou precisa saber que uma classe chamada
ArraySampler
mesmo existe. A classe poderia até ser resolvida em tempo de execução, e o local de chamada não seria o mais sábio. Portanto, consequentemente, sou livre para mudar a classeArraySampler
o quanto eu quiser., incluindo, mas não limitado a, a exclusão definitiva da classe, se, por exemplo, eu decidir que oScalarSampler
deve ser usado para dados array também. Só preciso de mudar a linha.samplers.put(SamplingType.Array, new ArraySampler());
A
E funcionaria magicamente. Não tenho de alterar uma única linha de código nas aulas de chamada, que poderia ser de centenas. Efetivamente, a fábrica me faz controlar o que e como a amostragem ocorre, e quaisquer mudanças de amostragem são eficientemente encapsuladas dentro de um único classe de fábrica que está conectado com o resto do sistema.samplers.put(SamplingType.Array, new ScalarSampler());
Pessoalmente, eu uso o padrão de fábrica quando a implementação de uma interface é desconhecida no tempo de execução, ou pode ser feita dinâmica.
Isso significa que como desenvolvedor, eu trabalho contra uma interface conhecida para a instância do objeto, mas não estou preocupado com a forma como a implementação funciona.
Por exemplo. Você poderia usar um padrão de fábrica para lhe fornecer objetos de uma base de dados. Você não se importa se essa base de dados é um arquivo Plano, Base de dados local/único usuário, servidor banco de dados ou recurso web, apenas que a fábrica pode gerar e gerenciar esses objetos. Odeio ter de escrever implementações para cada um desses casos.Do eficaz livro Java de Joshua Bloch, parcialmente reescrito por mim:
1) Os métodos estáticos de fábrica (SFM), ao contrário dos construtores, têm nomes.
public static ComplexNumber one () {
return new ComplexNumber(1, 0);
}
public static ComplexNumber imgOne () {
return new ComplexNumber(0, 1);
}
public static ComplexNumber zero () {
return new ComplexNumber(0, 0);
}
2) não é necessário criar um novo objecto cada vez que SFM
é / são invocados
public static Boolean valueOf(boolean b) {
return b ? Boolean.TRUE : Boolean.FALSE;
}
3) SFM
pode devolver um objeto de qualquer subtipo do seu tipo de retorno.
4) SFM
reduza a verbosidade da criação de instâncias de tipo parametrizado.
public static <K, V> HashMap<K, V> newInstance() {
return new HashMap<K, V>();
}
Map<String, List<String>> m = HashMap.newInstance();
Para complementar a resposta de Thilo, vamos supor que você tem um objeto que só tem um booleano como um construtor: seria um desperdício total construir um de cada vez, uma vez que ele só tem dois valores possíveis.
Neste caso, você pode criar métodos de fábrica estáticos. A classeBoolean
do Java é um exemplo: Boolean.valueOf()
.
Você pode se referir à Wikipédia , mas a ideia básica da maioria dos padrões de design é introduzir alguma abstração para conseguir uma melhor manutenção e/ou reutilização. O padrão do método de fábrica não é exceção, o que ele faz é abstrair a complexidade da criação do Código.
Para um caso simples parece desnecessário usar o padrão de fábrica, um simples {[[0]} é suficiente. Mas quando você precisa de mais flexibilidade ou funcionalidade, este padrão pode ajudar.
Por exemplo, a menos que uma nova instância seja necessária, a static factory valueOf(boolean)
é geralmente uma escolha melhor do que new Bealean(boolean)
, pois evita a criação de objetos desnecessários. Factory method pattern is also known as Virtual Constructor . Como sabemos, o polimorfismo é uma das principais características da OOP, mas o construtor não pode ser polimórfico, esta lacuna pode ser superada pelo padrão do método de fábrica.
Em essência, instanciar um objecto directamente (normalmente através de new
) é apenas um betão. implementação, enquanto método de fábrica padrão de escudos de um volátil implementação por estável interface(não limitado a, interface
em Java), empurrando a lógica do objeto-a criação de trás de alguma abstração para garantir mais fácil de manter e de código reutilizável.
Como uma palavra final, para compreender plenamente o benefício do padrão do método de fábrica e de outros padrões de design, é preciso compreender a essência da OOP, incluindo a abstração de dados , abstracção polimórfica e princípio Sólido .