Java - É uma má idéia ter classes totalmente estáticas?

14

Estou trabalhando em um projeto solo maior e agora, e tenho várias classes nas quais não vejo nenhum motivo para criar uma instância.

Minha classe de dados agora, por exemplo, armazena todos os seus dados estaticamente e todos os seus métodos são estáticos também. Eu não preciso inicializá-lo porque quando eu quero rolar os dados e obter um novo valor, eu uso apenas Dice.roll() .

Eu tenho várias classes similares que têm apenas uma função principal como essa e estou prestes a começar a trabalhar em um tipo de classe "controladora" que será responsável por todos os eventos (como quando um jogador se move e qual é a curva atual) e descobri que poderia seguir a mesma ideia para essa classe. Eu nunca planejo criar vários objetos para essas classes específicas, então seria uma má idéia torná-los totalmente estáticos?

Eu queria saber se isso é considerado "má prática" quando se trata de Java. Pelo que eu vi, a comunidade parece estar meio que dividida neste tópico? De qualquer forma, eu adoraria alguma discussão sobre isso e links para recursos também seriam ótimos!

    
por HexTeke 11.04.2018 / 08:10
fonte

4 respostas

19

Não há nada errado com as classes estáticas que são realmente static . Ou seja, não há nenhum estado interno para falar que faria com que a saída dos métodos mudasse.

Se Dice.roll() estiver simplesmente retornando um novo número aleatório de 1 a 6, ele não está mudando de estado. Concedido, você pode estar compartilhando uma instância de Random , mas eu não consideraria isso uma mudança de estado, pois, por definição, a saída sempre estará bem, aleatória. Também é thread-safe, então não há problemas aqui.

Você verá com frequência o "Ajudante" final ou outras classes de utilitário que tenham um construtor privado e membros estáticos. O construtor privado não contém lógica e serve apenas para impedir que alguém instancie a classe. O modificador final só traz essa idéia para casa de que essa não é uma classe que você gostaria de obter. É meramente uma classe de utilidade. Se feito corretamente, não deve haver nenhum singleton ou outros membros da classe que não sejam eles mesmos estáticos e finais.

Contanto que você siga estas diretrizes e não esteja fazendo singletons, não há absolutamente nada de errado nisso. Você menciona uma classe controladora, e isso praticamente certamente exige mudanças de estado, portanto, aconselho não usar somente métodos estáticos. Você pode confiar strongmente em uma classe de utilitário estático, mas você não pode torná-lo uma classe de utilitário estático.

O que é considerado uma alteração no estado para uma aula? Bem, vamos excluir números aleatórios por um segundo, pois eles não são determinísticos por definição e, portanto, o valor de retorno muda com frequência.

Uma função pura é aquela que é determinística, ou seja, para uma dada entrada, você obterá uma e exatamente uma saída. Você quer que os métodos estáticos sejam funções puras. Em Java, existem maneiras de ajustar o comportamento de métodos estáticos para manter o estado, mas quase nunca são boas idéias. Quando você declara um método como estático , o programador típico assumirá imediatamente que é uma função pura. Desviar-se do comportamento esperado é como você tende a criar erros no seu programa, em geral, e deve ser evitado.

Um singleton é uma classe que contém métodos estáticos quase opostos à "função pura", como você pode ser. Um único membro privado estático é mantido internamente na classe que é usada para garantir que exista exatamente uma instância. Esta não é uma prática recomendada e pode causar problemas mais tarde por vários motivos. Para saber do que estamos falando, aqui está um exemplo simples de um singleton:

// DON'T DO THIS!
class Singleton {
  private String name; 
  private static Singleton instance = null;

  private Singleton(String name) {
    this.name = name;
  }

  public static Singleton getInstance() {
    if(instance == null) {
      instance = new Singleton("George");
    }
    return instance;
  }

  public getName() {
    return name;
  }
}

assert Singleton.getInstance().getName() == "George"
    
por 11.04.2018 / 08:30
fonte
9

Para dar um exemplo das limitações de uma classe static , e se alguns de seus jogadores quiserem obter um pequeno bônus em suas jogadas de dados? E eles estão dispostos a pagar muito dinheiro! : -)

Sim, você pode adicionar outro parâmetro, então Dice.roll(bonus) ,

Mais tarde você precisa do D20s.

Dice.roll(bonus, sides)

Sim, mas alguns jogadores têm o talento "supremamente capaz", então eles nunca podem "atrapalhar" (rolar um 1).

Dice.roll(bonus, sides, isFumbleAllowed) .

Isso está ficando confuso, não é?

    
por 11.04.2018 / 16:33
fonte
3

No caso particular de uma classe Dice, acho que usar métodos de instância em vez de estática tornará o teste significativamente mais fácil.

Se você quiser testar algo que usa uma instância de Dice (por exemplo, uma classe de jogo), então, a partir de seus testes, você pode injetar alguma forma de teste duplo de dados que sempre retorna uma seqüência fixa de valores. Seu teste pode verificar se o jogo tem o resultado certo para as jogadas de dados.

Eu não sou um desenvolvedor Java, mas acho que seria significativamente mais difícil fazer isso com uma classe Dice totalmente estática. Consulte o link

    
por 11.04.2018 / 22:01
fonte
0

Na verdade, isso é conhecido como Monostate pattern , em que todas as instâncias (e até mesmo uma "no-instance") compartilham seu status. Cada membro é um membro da turma (ou seja, nenhum membro da instância). Eles são comumente usados para implementar classes de "kits de ferramentas" que agrupam um conjunto de métodos e constantes relacionados a uma única responsabilidade ou requisito, mas não precisam de um estado para funcionar (eles são puramente funcionais). Na verdade, o Java vem com alguns deles empacotados (por exemplo, Math ).

Um pouco fora do tópico: eu raramente concordo com a nomeação de palavras-chave no VisualBasic, mas, neste caso, acho que shared é certamente mais claro e semanticamente melhor (é compartilhado entre a classe propriamente dito e todas as suas instâncias) que static (permanece após o ciclo de vida do escopo ser declarado).

    
por 11.04.2018 / 20:54
fonte