Há alguns meses, comecei a trabalhar em um novo projeto e, ao analisar o código, percebi a quantidade de métodos estáticos usados. Não apenas métodos utilitários collectionToCsvString(Collection<E> elements)
, mas também muita lógica de negócios é mantida neles.
Quando perguntei ao sujeito responsável pela lógica por trás disso, ele disse que era uma maneira de escapar da tirania de Spring . É algo em torno desse processo de pensamento: para implementar um método de criação de recibos de clientes, poderíamos ter um serviço
@Service
public class CustomerReceiptCreationService {
public CustomerReceipt createReceipt(Object... args) {
CustomerReceipt receipt = new CustomerReceipt();
// creation logic
return receipt;
}
}
Agora, o sujeito disse que não gosta de ter classes desnecessariamente gerenciadas pelo Spring, basicamente porque impõe a restrição de que as classes clientes devam ser elas próprias. Acabamos tendo tudo gerenciado pelo Spring, que praticamente nos obriga a trabalhar com objetos sem estado de maneira processual. Mais ou menos o que está indicado aqui https://www.javacodegeeks.com/2011/02/domain-driven-design-spring-aspectj.html
Então, em vez do código acima, ele tem
public class CustomerReceiptCreator {
public static CustomerReceipt createReceipt(Object... args) {
CustomerReceipt receipt = new CustomerReceipt();
// creation logic
return receipt;
}
}
Eu poderia argumentar a ponto de evitar que o Spring gerencie nossas aulas sempre que possível, mas o que não vejo é o benefício de ter tudo estático. Esses métodos estáticos também são sem estado, portanto, também não são muito OO. Eu me sentiria mais confortável com algo como
new CustomerReceiptCreator().createReceipt()
Ele afirma que os métodos estáticos têm alguns benefícios extras. Nomeadamente:
- Mais fácil de ler. Importe o método estático e precisamos nos preocupar apenas com a ação, não qual a classe que está fazendo isso.
- É obviamente um método livre de chamadas de banco de dados, tão barato em termos de desempenho; e é bom deixar claro, para que o cliente em potencial precise entrar no código e verificar isso.
- Mais fácil de escrever testes.
Mas sinto que há algo que não está completamente certo nisso, então gostaria de ouvir alguns pensamentos de desenvolvedores mais experientes sobre isso.
Então, minha pergunta é: quais são as possíveis armadilhas dessa maneira de programação?
fonte
static
método que você está ilustrando acima é apenas um método comum de fábrica. Tornar estáticos os métodos de fábrica é a convenção geralmente aceita, por vários motivos convincentes. Se o método de fábrica é apropriado aqui é outra questão.Respostas:
Qual é a diferença entre
new CustomerReceiptCreator().createReceipt()
eCustomerReceiptCreator.createReceipt()
? Praticamente nenhum. A única diferença significativa é que o primeiro caso tem uma sintaxe consideravelmente mais estranha. Se você segue o primeiro na crença de que, de alguma forma, evitar métodos estáticos torna seu código melhor OO, você está muito enganado. Criar um objeto apenas para chamar um método único é um método estático por sintaxe obtusa.Onde as coisas ficam diferentes é quando você injeta, em
CustomerReceiptCreator
vez de injetarnew
. Vamos considerar um exemplo:Vamos comparar esta versão do método estático:
A vantagem da versão estática é que eu posso lhe dizer rapidamente como ela interage com o resto do sistema. Não faz. Se eu não usei variáveis globais, sei que o resto do sistema não foi alterado de alguma forma. Sei que nenhuma outra parte do sistema pode afetar o recebimento aqui. Se usei objetos imutáveis, sei que a ordem não mudou e o createReceipt é uma função pura. Nesse caso, posso mover / remover / etc livremente essa chamada sem me preocupar com efeitos imprevisíveis aleatórios em outros lugares.
Não posso fazer as mesmas garantias se injetar o
CustomerReceiptCreator
. Pode ter um estado interno que é alterado pela chamada, posso ser afetado ou alterar outro estado. Pode haver relações imprevisíveis entre declarações em minha função, de modo que alterar a ordem introduza bugs surpreendentes.Por outro lado, o que acontece se de
CustomerReceiptCreator
repente precisar de uma nova dependência? Digamos que ele precise verificar um sinalizador de recurso. Se estivéssemos injetando, poderíamos fazer algo como:Então terminamos, porque o código de chamada será injetado com um
CustomerReceiptCreator
que será automaticamente injetado aFeatureFlags
.E se estivéssemos usando um método estático?
Mas espere! o código de chamada também precisa ser atualizado:
Obviamente, isso ainda deixa a questão de onde
processOrder
elaFeatureFlags
vem. Se tivermos sorte, a trilha termina aqui; caso contrário, a necessidade de passar pelo FeatureFlags é aumentada ainda mais na pilha.Há uma troca aqui. Métodos estáticos que exigem a passagem explícita das dependências, o que resulta em mais trabalho. O método injetado reduz o trabalho, mas torna as dependências implícitas e, assim, ocultas, dificultando o raciocínio do código.
fonte