Em alguns dos meus códigos, tenho uma fábrica estática semelhante a esta:
public class SomeFactory {
// Static class
private SomeFactory() {...}
public static Foo createFoo() {...}
public static Foo createFooerFoo() {...}
}
Durante uma revisão de código, foi proposto que este fosse um singleton e injetado. Portanto, deve ficar assim:
public class SomeFactory {
public SomeFactory() {}
public Foo createFoo() {...}
public Foo createFooerFoo() {...}
}
Algumas coisas a destacar:
- Ambas as fábricas são apátridas.
- A única diferença entre os métodos são seus escopos (instância vs estática). As implementações são as mesmas.
- Foo é um bean que não possui uma interface.
Os argumentos que tive para ficar estático foram:
- A classe é sem estado, portanto, não precisa ser instanciada
- Parece mais natural poder chamar um método estático do que ter que instanciar uma fábrica
Os argumentos para a fábrica como um singleton foram:
- É bom injetar tudo
- Apesar da apatridia da fábrica, o teste é mais fácil com a injeção (fácil de zombar)
- Deve ser ridicularizado ao testar o consumidor
Eu tenho alguns problemas sérios com a abordagem singleton, pois parece sugerir que nenhum método deve ser estático. Também parece sugerir que utilitários como StringUtils
devem ser embalados e injetados, o que parece bobagem. Por fim, isso implica que vou precisar zombar da fábrica em algum momento, o que não parece certo. Não consigo pensar em quando precisaria zombar da fábrica.
O que a comunidade pensa? Embora eu não goste da abordagem singleton, não pareço ter um argumento muito forte contra isso.
fonte
DateTime
eFile
são notoriamente difíceis de testar pelas mesmas razões. Se você tem uma classe, por exemplo, que define aCreated
dataDateTime.Now
no construtor, como criar um teste de unidade com dois desses objetos que foram criados com 5 minutos de intervalo? E quanto a anos separados? Você realmente não pode fazer isso (sem muito trabalho).private
construtor e umgetInstance()
método? Desculpe, nit-picker incorrigível!Respostas:
Por que você separaria sua fábrica do tipo de objeto que ela cria?
Isso é o que Joshua Bloch descreve como seu Item 1 na página 5 de seu livro "Java Efetivo". Java é detalhado o suficiente sem adicionar classes e singletons extras e outros enfeites. Existem alguns casos em que uma classe de fábrica separada faz sentido, mas são poucas e distantes entre si.
Parafraseando o Item 1 de Joshua Bloch, diferentemente dos construtores, os métodos estáticos de fábrica podem:
Desvantagens:
Realmente, você deve levar o livro de Bloch ao revisor de código e ver se vocês dois podem concordar com o estilo de Bloch.
fonte
public static IFoo of(...) { return new Foo(...); }
Qual é o problema? Bloch lista a satisfação de sua preocupação como uma das vantagens desse design (segundo ponto acima). Não devo estar entendendo o seu comentário.Logo de cara, aqui estão alguns dos problemas com estática em Java:
métodos estáticos não são compatíveis com as "regras" do OOP. Você não pode forçar uma classe a implementar métodos estáticos específicos (tanto quanto eu sei). Portanto, você não pode ter várias classes implementando o mesmo conjunto de métodos estáticos com as mesmas assinaturas. Então você está preso a um dos itens que está fazendo. Você também não pode subclassificar uma classe estática. Portanto, sem polimorfismo, etc.
Como os métodos não são objetos, os métodos estáticos não podem ser contornados e não podem ser usados (sem fazer uma tonelada de codificação padrão), exceto pelo código que está vinculado a eles - o que torna o código do cliente altamente acoplado. (Para ser justo, isso não é apenas culpa da estática).
campos estáticos são bastante semelhantes aos globais
Você mencionou:
O que me deixa curioso para perguntar:
StringUtils
foi corretamente projetado e implementado?fonte
Foo f = createFoo();
vez de ter uma instância nomeada. A instância em si não fornece utilidade. (2) Eu acho que sim. Ele foi projetado para superar o fato de que muitos desses métodos não existem naString
classe. Substituir algo tão fundamental quantoString
não é uma opção, portanto, os utilitários são o caminho a percorrer. (continuação)Integer
. Eles são muito simples, têm muito pouca lógica e existem apenas para oferecer conveniência (por exemplo, não há outro motivo de OO para tê-los). A parte principal do meu argumento é que eles não dependem de nenhum estado - não há razão para isolá-los durante os testes. As pessoas não zombamStringUtils
durante o teste - por que precisariam zombar dessa fábrica simples de conveniência?StringUtils
porque há uma garantia razoável de que os métodos não têm efeitos colaterais.StringUtils
retorna algum resultado sem alterar as entradas, minha fábrica também não modifica nada.Eu acho que o aplicativo que você está construindo deve ser bastante simples, ou você está relativamente adiantado em seu ciclo de vida. O único outro cenário em que posso pensar em que você já não teria problemas com sua estatística é se você conseguiu limitar as outras partes do seu código que conhecem sua fábrica a um ou dois lugares.
Imagine que seu Aplicativo evolua e agora, em alguns casos, você precisa produzir uma
FooBar
(subclasse de Foo). Agora você precisa percorrer todos os lugares do seu código que conhecem o seuSomeFactory
e escrever algum tipo de lógica que analisesomeCondition
e chameSomeFactory
ouSomeOtherFactory
. Na verdade, olhando para o seu código de exemplo, é pior do que isso. Você planeja apenas adicionar método após método para criar Foos diferentes e todo o seu código de cliente deve verificar uma condição antes de descobrir qual Foo fazer. O que significa que todos os clientes precisam ter um conhecimento íntimo de como sua fábrica funciona e todos precisam mudar sempre que um novo Foo for necessário (ou removido!).Agora imagine se você acabou de criar um
IFooFactory
que cria umIFoo
. Agora, produzir uma subclasse diferente ou mesmo uma implementação completamente diferente é brincadeira de criança - você apenas alimenta o IFooFactory certo na cadeia e, quando o cliente obtém, ele chamagimmeAFoo()
e obtém o resultado certo porque obteve a Fábrica certa. .Você pode estar se perguntando como isso ocorre no código de produção. Para dar um exemplo da base de código em que estou trabalhando, que está começando a se estabilizar após pouco mais de um ano de projetos em andamento, tenho várias Fábricas usadas para criar objetos de dados de perguntas (são como modelos de apresentação ) Eu tenho um
QuestionFactoryMap
que é basicamente um hash para procurar qual fábrica de perguntas usar quando. Então, para criar um Aplicativo que faça perguntas diferentes, coloquei Fábricas diferentes no mapa.As perguntas podem ser apresentados em qualquer instruções ou exercícios (que tanto implementar
IContentBlock
), de modo que cadaInstructionsFactory
ouExerciseFactory
receberá uma cópia do QuestionFactoryMap. Em seguida, as várias fábricas de Instruções e Exercícios são entregues àsContentBlockBuilder
quais, novamente, mantém um hash do qual usar quando. E então, quando o XML é carregado, o ContentBlockBuilder chama a Fábrica de Exercícios ou Instruções a partir do hash e solicitacreateContent()
, e a Fábrica delega a construção das perguntas para o que extrai de seu QuestionFactoryMap interno, com base no que está em cada nó XML vs. o hash. Infelizmente , essa coisa é um emBaseQuestion
vez de umIQuestion
, mas isso só mostra que mesmo quando você toma cuidado com o design, pode cometer erros que podem assombrá-lo.Seu intestino está lhe dizendo que essas estáticas vão machucá-lo, e certamente há muito na literatura para apoiá-lo. Você nunca perderá com a Injeção de Dependência que acaba usando apenas para seus Testes de Unidade (não que eu acredite que, se você criar um bom código, não conseguirá usá-lo mais do que isso), mas a estática pode destruir completamente sua capacidade para modificar seu código de maneira rápida e fácil. Então, por que ir até lá?
fonte
Integer
classe - coisas simples como converter de uma String. Isso é o queFoo
faz. MeuFoo
objetivo não é ser estendido (minha culpa por não declarar isso), portanto, não tenho seus problemas polimórficos. Entendo o valor de ter fábricas polimórficas e as usei no passado ... só não acho que sejam apropriadas aqui. Você pode imaginar se você precisar instanciar uma fábrica para executar as operações simplesInteger
que atualmente são instaladas através de fábricas estáticas?Foo
, no entanto, é muito mais simples do que muitas das classes. É apenas um POJO simples.if (this) then {makeThisFoo()} else {makeThatFoo()}
todo o código. Uma coisa que notei sobre pessoas com arquitetura crônica ruim é que elas são como pessoas com dor crônica. Na verdade, eles não sabem como é não sentir dor; portanto, a dor em que sentem não parece tão ruim assim.Foo
. Com razão - nunca o declarei final.Foo
é um bean que não deve ser estendido.