Recentemente, fui lançado em um projeto de aplicativo da web Java e deparei-me com várias classes que seguem esse tipo de formato:
public class MyThingy {
private final int p1;
private final String p2;
…
public MyThingy (int p1, String p2, …) {
this.p1 = p1;
this.p2 = p2;
…
}
public static void doSomething(int p1, String p2, …) throws Throwable {
final MyThingy myThingy = new MyThingy(p1, p2, …);
myThingy.execute();
}
private void execute() throws Throwable {
//do stuff
}
}
Parece que isso poderia ser realizado com o código a seguir, que para mim parece muito mais fácil de ler.
public class MyThingy {
public static void doSomething (int p1, String p2, …) throws Throwable {
//do stuff
}
}
O único benefício possível que vejo ao fazê-lo da primeira maneira é que, se você tivesse que dividir execute () em partes menores, todos eles poderiam compartilhar os parâmetros iniciais sem precisar explicitamente explicá-los. Mas isso talvez beneficie apenas o codificador lento, pois fica difícil para o leitor dizer quais métodos precisam de quais parâmetros e quando os valores podem ser alterados (semelhante a variáveis globais).
Tem algo que estou perdendo? Rosqueamento, desempenho?
Edit: Eu deveria ter mencionado, embora o construtor seja público, não é chamado. O único uso é assim:
MyThingy.doSomething(p1, p2...);
Além de ser problemático para o teste, não vejo motivo para não colocar a lógica de execute () diretamente em doSomething (). Mesmo se nos livrássemos da função estática, o construtor ainda não faz sentido para mim; Eu acho que os parâmetros devem ser passados diretamente para o método que os usará.
fonte
throws Throwable
é uma prática ruim, deve ser pelo menosthrows Exception
ou algo mais específico, se possível. E, como as práticas ruins geralmente se reúnem, eu diria que esse modelo de código é apenas outra prática ruim.new(...)
+execute()
em uma chamada.final
). O método estático é a única API no objeto, pois todos os membros são privados. Em apenas o código que você compartilhou, eu não vejo nenhum benefício para este sobre tornandoexecute
um método estático tomar p1, p2, ....Respostas:
Penso que a sua intuição sobre esta questão está certa.
Então, você deveria usá-lo ?
De fato, a questão não é apenas "métodos como classes" puros. Nas classes normais, você pode ser tentado a criar campos que não mantêm o estado entre invocações de métodos públicos. Eles são usados apenas para evitar a passagem de parâmetros entre métodos privados. No passado, tentei evitá-lo a todo custo, mas depois percebi que longas listas de parâmetros também eram ruins.
Eu tento evitar pura "classes de métodos" , pois acho que elas vêm com muita bagagem mental (muito potencial em potencial). (Eu também me apaixono por campos descartáveis nas aulas regulares.) Mas se houver uma tonelada de variáveis para distribuir, a vitória na limpeza do código pode valer a pena.
Eu tento usar as classes quando há um ponto, e na verdade geralmente é fácil imaginar uma. Talvez no futuro você estenda a classe com implementações adicionais. Talvez a instância possa ser pensada como um objeto de função que você deseja inicializar e distribuir. E então, a classe não é mais uma "classe de método" pseudo-OO.
fonte
A indireção extra por meio da chamada de método estático separa as preocupações de como um objeto é criado do código que usa o objeto. O que você tem aqui é algo muito semelhante a um método simples de fábrica (na verdade, ele não retorna o objeto, mas o indireto da criação do objeto é o mesmo).
Isso pode ser útil se você precisar controlar o tipo de objeto criado. Neste exemplo simples, não há decisão a ser tomada, mas seria fácil adicionar essa lógica.
O que o código estático significa para os chamadores é "preciso fazer algo com esse estado, mas não sei como delegar". O método estático pode delegar para a implementação adequada com base no objeto fornecido.
Talvez ao executar um teste de unidade exista um objeto falso sendo usado. Talvez com base nos parâmetros em que um objeto diferente possa ser trocado, implementando um algoritmo diferente para fazer o que for necessário. Ao localizar esse código em um local, a decisão pode ser tomada em um local e não arbitrariamente em muitos.
Dada a forma como sua pergunta está formulada e o fato de que nenhuma decisão de construção está sendo tomada nos métodos estáticos, sinto que isso pode ser apenas um caso de excesso de engenharia. A maioria dos códigos não precisa delegar para uma fábrica. O fato de você continuar encontrando um código como esse que não toma nenhuma decisão, como eu esperaria que um método estático semelhante a uma fábrica fizesse, me diz que alguém leu o livro do GoF e ficou louco.
fonte
Bem, se o seu segundo exemplo (mostrando o código completo ) se parecer com o seguinte:
então você ainda precisará do construtor
que por sua vez define
e agora você está de volta onde começou.
Obviamente, você poderia simplesmente eliminar o construtor e escolher getters e setters
... mas isso complicará sua chamada de método estático, já que agora você precisará de várias linhas de código para chamar os setters antes de chamar seu método estático, onde uma única linha que utilizasse a chamada de construtor seria suficiente.
Obviamente, isso só funciona se suas variáveis de membro não estiverem
final
. Portanto, você ainda precisará do construtor.fonte
final
variável. Só pode ser inicializado uma vez.Normalmente, uma classe possui muitos métodos de instância e, uma vez que o objeto é criado, a implementação de execute pode tirar proveito de todos esses métodos de instância. Se você se recusar a criar um objeto, todos esses métodos de instância serão inúteis.
fonte
execute
. Assim, perguntando por que não alinhar a coisa toda.