O padrão de fábrica (ou pelo menos o uso de FactoryFactory..
) é alvo de muitas piadas, como aqui .
Além de ter nomes detalhados e "criativos", como RequestProcessorFactoryFactory.RequestProcessorFactory , há algo de fundamentalmente errado com o padrão de fábrica se você precisar programar em Java / C ++ e houver uma base de dados para Abstract_factory_pattern ?
Como outro idioma popular (por exemplo, Ruby ou Scala ) evitava usá-lo enquanto gerenciava uma complexidade semelhante?
A razão pela qual estou perguntando é que vejo principalmente as críticas às fábricas mencionadas no contexto do ecossistema Java / Java EE , mas elas nunca explicam como outras linguagens / estruturas as resolvem.
java
design-patterns
senseiwu
fonte
fonte
Respostas:
Sua pergunta está marcada com "Java", não é surpresa que você esteja se perguntando por que o padrão Factory está sendo ridicularizado: o próprio Java vem com abusos bem embalados desse padrão.
Por exemplo, tente carregar um documento XML de um arquivo e execute uma consulta XPath nele. Você precisa de algo como 10 linhas de código apenas para configurar os Fábricas e Construtores:
Eu me pergunto se os caras que criaram essa API já trabalharam como desenvolvedores ou apenas leram livros e jogaram coisas por aí. Eu entendo que eles simplesmente não queriam escrever um analisador e deixaram a tarefa para os outros, mas ainda é uma implementação feia.
Como você está perguntando qual é a alternativa, aqui está o carregamento do arquivo XML em C #:
Suspeito que desenvolvedores de Java recém-nascidos vejam a loucura da Factory e acho que é bom fazer isso - se os gênios que construíram o Java os usaram muito.
A fábrica e quaisquer outros padrões são ferramentas, cada uma adequada para um trabalho específico. Se você aplicá-los a trabalhos que não são adequados, você provavelmente terá um código feio.
fonte
XmlDocument xml = new XmlDocument(); xml.Load("c:\\employees.xml"); XmlNodeList nodes = xml.SelectNodes(expression);
(No geral, o LINQ to XML é muito mais fácil de usar, embora principalmente em outras áreas.) E aqui está um artigo da KB I encontrado, com um método recomendadas MS-: support.microsoft.com/kb/308333Como tantas vezes, as pessoas entendem mal o que está acontecendo (e isso inclui muitos dos risos).
Não é o padrão de fábrica em si que é tão ruim quanto o modo como muitas (talvez até a maioria) as pessoas o usam.
E isso sem dúvida decorre da maneira como a programação (e os padrões) são ensinados. Os alunos (geralmente chamados de "estudantes") são instruídos a "criar X usando o padrão Y" e, depois de algumas iterações, pensam que esse é o caminho para abordar qualquer problema de programação.
Então eles começam a aplicar um padrão específico de que gostam na escola contra tudo e qualquer coisa, seja apropriado ou não.
E isso inclui professores universitários que escrevem livros sobre design de software, infelizmente.
O ponto culminante disso foi um sistema que eu tinha o distinto desagrado de manter, criado por um desses (o homem tinha até vários livros sobre design orientado a objetos em seu nome e uma posição de professor no departamento de CS de uma grande universidade). )
Foi baseado em um padrão de três camadas, com cada camada em si um sistema de três camadas (é necessário desacoplar ...). Na interface entre cada conjunto de camadas, em ambos os lados, havia uma fábrica para produzir os objetos para transferir dados para a outra camada e uma fábrica para produzir o objeto para converter o objeto recebido em um pertencente à camada de recebimento.
Para cada fábrica, havia uma fábrica abstrata (quem sabe, a fábrica pode precisar mudar e você não deseja que o código de chamada precise mudar ...).
E toda essa bagunça era obviamente completamente sem documentos.
O sistema tinha um banco de dados normalizado para a quinta forma normal (não estou brincando).
Um sistema que era essencialmente pouco mais que um catálogo de endereços que poderia ser usado para registrar e rastrear documentos recebidos e enviados, tinha uma base de código de 100 MB em C ++ e mais de 50 tabelas de banco de dados. A impressão de 500 cartas levaria 72 horas usando uma impressora conectada diretamente ao computador executando o software.
É por isso que as pessoas zombam dos padrões e, especificamente, as pessoas que se concentram isoladamente em um único padrão específico.
fonte
As fábricas têm muitas vantagens que permitem o design elegante de aplicativos em algumas situações. Uma é que você pode definir as propriedades dos objetos que deseja criar posteriormente em um só lugar, criando uma fábrica e depois entregá-la. Mas muitas vezes você realmente não precisa fazer isso. Nesse caso, o uso de uma fábrica apenas adiciona complexidade adicional sem fornecer nada em troca. Vamos pegar esta fábrica, por exemplo:
Uma alternativa ao padrão Factory é o padrão Builder muito semelhante. A principal diferença é que as propriedades dos objetos criados por um Factory são definidas quando o Factory é inicializado, enquanto um Builder é inicializado com um estado padrão e todas as propriedades são definidas posteriormente.
Mas quando a superengenharia é o seu problema, substituir uma fábrica por um construtor provavelmente não é uma grande melhoria.
A substituição mais simples para qualquer padrão é, obviamente, criar instâncias de objetos com um construtor simples com o
new
operador:Os construtores, no entanto, têm uma desvantagem crucial na maioria das linguagens orientadas a objetos: eles devem retornar um objeto dessa classe exata e não podem retornar um subtipo.
Quando você precisar escolher o subtipo em tempo de execução, mas não desejar recorrer à criação de uma nova classe Builder ou Factory para isso, poderá usar um método de fábrica. Este é um método estático de uma classe que retorna uma nova instância dessa classe ou de uma de suas subclasses. Uma fábrica que não mantém nenhum estado interno geralmente pode ser substituída por um método de fábrica:
Um novo recurso no Java 8 são as referências de métodos que permitem transmitir métodos, como faria com uma fábrica sem estado. Convenientemente, qualquer coisa que aceite uma referência de método também aceita qualquer objeto que implemente a mesma interface funcional, que também pode ser uma Fábrica de pleno direito com estado interno, para que você possa facilmente introduzir fábricas mais tarde, quando perceber uma razão para fazê-lo.
fonte
foo = new Bar(23);
seja equivalente afoo = Bar._createInstance(23);
. Um objeto deve poder consultar seu próprio tipo de maneira confiável usando umgetRealType()
método privado , mas os objetos devem poder designar um supertipo a ser retornado por chamadas externas paragetType()
. O código externo não deve se importar se uma chamada paranew String("A")
realmente retorna uma instância deString
[em oposição a, por exemplo, uma instância deSingleCharacterString
].draw(OutputStream out)
mas geram html um pouco diferente, a fábrica de métodos estáticos cria a classe certa para a situação e o chamador pode apenas usar o método draw.[[SomeClass alloc] init]
. É possível retornar uma classe instância completamente diferente ou outro objeto (como um valor em cache)Fábricas de um tipo ou de outro são encontradas em praticamente qualquer linguagem orientada a objetos em circunstâncias apropriadas. Às vezes, você simplesmente precisa de uma maneira de selecionar que tipo de objeto criar com base em um parâmetro simples, como uma string.
Algumas pessoas levam isso muito longe e tentam arquitetar seu código para nunca precisar chamar um construtor, exceto dentro de uma fábrica. As coisas começam a ficar ridículas quando você tem fábricas.
O que me impressionou quando aprendi Scala, e não conheço Ruby, mas acredito que é da mesma maneira, é que a linguagem é expressiva o suficiente para que os programadores nem sempre estejam tentando empurrar o trabalho de "encanamento" para arquivos de configuração externos . Em vez de ser tentado a criar fábricas de fábrica para conectar suas classes em diferentes configurações, no Scala você usa objetos com características misturadas. Também é relativamente fácil criar DSLs simples na linguagem para tarefas que geralmente são superdimensionadas em Java.
Além disso, como outros comentários e respostas apontaram, fechamentos e funções de primeira classe evitam a necessidade de muitos padrões. Por esse motivo, acredito que muitos dos antipadrões começarão a desaparecer à medida que o C ++ 11 e o Java 8 obtiverem ampla adoção.
fonte
Em geral, existe uma tendência de que os programas Java sejam terrivelmente superprojetados [citação necessário]. Ter muitas fábricas é um dos sintomas mais comuns do excesso de engenharia; é por isso que as pessoas tiram sarro disso.
Em particular, o problema que Java tem com as fábricas é que em Java a) os construtores não são funções eb) as funções não são cidadãos de primeira classe.
Imagine que você poderia escrever algo assim (que
Function
seja uma interface bem conhecida do JRE )Veja, não há interfaces ou classes de fábrica. Muitas linguagens dinâmicas possuem funções de primeira classe. Infelizmente, isso não é possível com o Java 7 ou anterior (implementar o mesmo com fábricas é deixado como um exercício para o leitor).
Portanto, a alternativa não é tanto "usar um padrão diferente", mas mais "usar uma linguagem com um modelo de objeto melhor" ou "não exagerar na engenharia de problemas simples".
fonte