Funções estáticas versus classes

8

Digamos que eu queira criar algumas funções utilitárias para fazer algumas matemáticas básicas com BigDecimals, por exemplo, eu quero ter uma função que calcule a média de a List<BigDecimal>.

Qual é a melhor abordagem? Uma função estática ou uma classe de utilidade?

public static BigDecimal computeAverage(List<BigDecimal> numbers)

ou

public class BigDecimalUtil

public computeAverage(List<BigDecimal> numbers)
S4M
fonte
Um ponto a considerar é identificar qual classe tem a responsabilidade de fazer esse cálculo. Você pode descobrir que essa funcionalidade não é globalmente exigida por outras classes e, portanto, pode ser um método privado em 1 classe. Outra coisa a considerar são as definições estáticas e a segurança do encadeamento.
NoChance

Respostas:

9

Provavelmente vou ceder minhas inclinações C # da pior maneira possível, mas sempre recomendo métodos estáticos nas classes de utilidade. Isso oferece o melhor dos dois mundos. Os benefícios organizacionais da classe com a usabilidade dos métodos acessados ​​sem a necessidade de instanciar um objeto para usá-los. Se a sua classe de utilitário tiver um motivo para ocasionalmente variar seu comportamento, você já estará bem posicionado para fazê-lo, introduzindo a herança / interfaces posteriormente, se necessário. Se você deseja que seu design seja um pouco mais portátil, verá que o paradigma será mais facilmente transferido para outras linguagens OO.

S.Robins
fonte
"Se a sua classe de utilitário tiver um motivo para ocasionalmente variar seu comportamento": sim, mas estou em uma loja de java e você não pode alterar um método estático estendendo a classe.
S4M
@ S4M Naturalmente, se o comportamento do seu método precisar mudar, será necessário repensar o design. Como eu disse na minha resposta, no entanto, se você precisar alterar o design, estará bem posicionado para fazê-lo. Você sempre pode refatorar o método para não-estático, se precisar que o comportamento seja estendido. Se você tiver todos os seus métodos espalhados por todo o lugar, terá dificuldade em localizá-los. Naturalmente, se o método for necessário apenas para uma única classe, seria melhor deixá-lo nessa classe; nesse caso, a questão de ser estático ou não se torna irrelevante.
S.Robins
Na verdade, sou um grande fã de métodos estáticos, mas na minha empresa notei um desenvolvedor sênior usando métodos não estáticos, então isso me fez pensar. Basicamente, eu gostaria de ListBigDecimalUtils.computeAverage (x) enquanto ele faria o novo AverageListBigDecimal (). Calcule (x). Não vejo o benefício de sua abordagem.
S4M
@ S4M Pode simplesmente permitir que ele forneça seus métodos com acesso a outros métodos não estáticos dentro de sua classe. Concedido, no entanto, que estes poderiam ser simplesmente métodos estáticos privados adicionais. O mais provável é que ele esteja se assegurando de deixar opções para estender a classe, caso veja uma necessidade no futuro. Talvez seja melhor perguntar ao desenvolvedor sênior qual é o raciocínio dele se você quiser entender onde ele vê os benefícios.
S.Robins
@ s4m. Eu acho que classes estáticas simples e não-diversas são boas e talvez a melhor escolha por enquanto (pena que o java não tenha o método de extensão btw). Mas um "serviço de estatísticas" também não é bobo. Eu poderia imaginar ter a necessidade de alterar Média de média para mediana para média aparada em tempo de execução. Posso imaginar operações matemáticas em listas de entradas que produzem entropia e exigiriam um serviço para testabilidade.
Nathan Cooper
5

Se você deseja executar funções utilitárias, conectadas por algum tema, crie uma classe para elas, torne-as estáticas nessa classe e torne um construtor dessa classe privado, para eliminar a possibilidade de ocorrência acidental de uma instância. Portanto, use a segunda variante, mas oculte o construtor.

public class BigDecimalUtil

public computeAverage(List<BigDecimal> numbers)

private BigDecimalUtil(){super();}

Há mais uma possibilidade - não fazer construtor e tornar essa classe abstrata. Mas, dessa maneira, você não pode usar a instância da classe não apenas fora, mas também dentro dela. Então, eu acho, essa segunda variante é muito rígida para você. Escolha você mesmo.

Gangnus
fonte
4

Concordo com o @Gangnus que uma classe estática "wrapper" faz sentido se métodos de classe individuais sempre serão usados ​​isoladamente, e isso geralmente será uma boa resposta.

No entanto, pode haver algum valor em uma classe não estática, com métodos não estáticos, se você tiver cálculos relacionados que provavelmente serão usados ​​juntos. Por exemplo, construir um ListStatsUtilobjecto com List<BigDecimal>passado para o construtor, pode ser conveniente para o acesso getStandardDeviation, getVariancee, getAverageem sequência, sem passar para trás no objecto. Isso pode ser mais elegante, mas também pode ter um desempenho melhor, especialmente se estiverem envolvidas operações computacionalmente caras que podem ser otimizadas entre operações, compartilhando o estado privado dentro do objeto.

public ListStatsUtil {
    private final List<BigDecimal> numbers;
    public ListStatsUtil(List<BigDecimal> numbers) {
        this.numbers = numbers;
    }
    public BigDecimal getAverage() {
        // return average of this.numbers
    }
    public BigDecimal getStandardDeviation() {
        // return std dev of this.numbers
    }
    public BigDecimal getVariance() {
        // return variance of this.numbers
    }
    public BigDecimal getMax() {
        // return max of this.numbers
    }
    public BigDecimal getMin() {
        // return min of this.numbers
    }
}
codingoutloud
fonte
Ainda não tenho permissão neste site para programadores para deixar um comentário para @Gangnus, por isso vou deixar aqui. O super()construtor in private faz sentido para uma classe estática em Java?
19412 codingoutloud
Sem dúvida, ele só tem sentido como uma autodisciplina no caso de uma refatoração radical futura. Queremos que esse construtor nunca seja executado.
Gangnus
Eu não entendo Todas essas funções, como getVariance e outras, recebem List <BigDecimal> e retornam BigDecimal de double. Como você pode colocá-los em cadeia? A idéia de encadear utilitários é boa, mas a) não para o caso fornecido por você. b) não no caso sobre qual é a questão. É o caso quando precisamos de uma nova classe com utilitários . Portanto, essa pergunta nunca surge - a resposta é óbvia.
Gangnus
Não está em uma cadeia, apenas que você pode querer várias operações na lista única. A questão não excluiu essa possibilidade.
Codingoutloud
A classe que descrevi pode definir facilmente esses métodos. Ele só não dá qualquer instância fora , mas pode usá-los dentro OK. Nesse caso, é claro, o construtor se torna funcional, mas nós nos preparamos para isso, não é? :-)
Gangnus
1

Votei na resposta do código em voz alta e gostaria de expandir um pouco.

O agrupamento de coleções nas aulas é algo que tentei sempre fazer - minha regra é "nunca passar uma coleção nua". As maiores falhas do OO que eu vi são quando as pessoas têm medo de adicionar métodos a que pertencem. No seu caso, os métodos pertencem à coleção (totalmente obviamente se você pensar sobre isso); portanto, coloque-os lá envolvendo a coleção e para colocar a cobertura no bolo, apenas exponha a funcionalidade necessária fora da sua nova classe (como codingoutloud fez) para que você possa ver sua classe única e pequena e entender facilmente tudo que manipula essa coleção.

Você não precisa resolver todos os problemas possíveis, porque é todo o seu código, você pode editar o wrapper de coleção com a mesma facilidade que você pode editar as outras classes que interagem com ele. Isso permite que você puxe outro código para a classe conforme necessário - se você nunca expuser a coleção, ficará rapidamente óbvio qual código pertence ao wrapper.

Isso também fornece controle total sobre a coleção, para que você possa impedir que ela entre em um estado inconsistente com sua função (por exemplo, se nenhuma entrada na coleção puder ser nula, apenas impeça que alguém coloque uma nula!)

A maior parte do código OO ruim que eu já vi foi criada porque alguém escolheu escrever utilitários estáticos ou código embutido que pertenciam a objetos aos quais não podiam adicionar código, em vez de agrupar os objetos ofensivos em um código próprio que eles poderiam modificar .

Bill K
fonte
Nesse caso em particular, eu diria que a média é um método razoável para invocar qualquer coleção de números. Assim, você pode acabar dando a todos os seus contêineres novos nomes com um pouco mais de funcionalidade. Parece um retorno ruim do esforço. É para isso que servem os métodos de extensão em c #, em java você está preso à estática.
Nathan Cooper
Utilitários sem lógica de negócios geralmente não têm uma boa solução OO e o melhor que você pode fazer é classes estáticas e classes fixas, como coleções. Se você realmente precisa fazer um utilitário de uso geral, também pode abandonar oo como coleções, utilitário de matemática e todos os outros utilitários. Nesse caso, ele provavelmente está apenas usando uma coleção para seu código comercial. Por que implementar uma solução geral?
Bill K
0

Se não estou errado e o Java considerar uma linguagem OO pura, você não pode ter funções sem agrupá-las em uma classe.

Portanto, sua resposta é: ambas, uma função estática agrupada em uma classe de utilidade estática, não muito diferente da classe Math.

Tulains Córdova
fonte
-2

Que tal uma interface CalcAverageStrategy que é injetada e, em seguida, você pode ter implementações de média / mediana / modo injetadas quando necessário.

Cada implementação é fácil de testar e também fácil de zombar para testes.

NimChimpsky
fonte
este lê mais como um comentário, consulte Como responder
mosquito