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 do.
Minha classe de dados agora, por exemplo, armazena todos os seus dados estaticamente e todos os seus métodos também são estáticos. Não preciso inicializá-lo porque, quando quero jogar os dados e obter um novo valor, apenas uso Dice.roll()
.
Eu tenho várias classes semelhantes que possuem apenas uma função principal como essa e estou prestes a começar a trabalhar em uma espécie de classe "controller" que será responsável por todos os eventos (como quando um jogador se move e qual a virada atual) é) e descobri que poderia seguir a mesma ideia para esta aula. Eu nunca planejo criar vários objetos para essas classes específicas, então seria uma má idéia torná-las totalmente estáticas?
Fiquei me perguntando se isso é considerado "má prática" quando se trata de Java. Pelo que vi, a comunidade parece meio que dividida sobre esse tópico? Enfim, eu adoraria alguma discussão sobre isso e os links para recursos também seriam ótimos!
Respostas:
Não há nada errado com as classes estáticas que são realmente estáticas . Ou seja, não há um estado interno para se falar, o que faria com que a saída dos métodos mudasse.
Se
Dice.roll()
simplesmente retornar um novo número aleatório de 1 a 6, não está mudando de estado. Concedido, você pode estar compartilhando umaRandom
instância, mas eu não consideraria que uma mudança de estado, por definição, a saída sempre será boa, aleatória. Também é seguro para threads, portanto não há problemas aqui.Você verá frequentemente "Helper" final ou outras classes de utilitário que têm um construtor privado e membros estáticos. O construtor privado não contém lógica e serve apenas para impedir que alguém instancia a classe. O modificador final apenas traz essa ideia de que essa não é uma classe da qual você deseja derivar. É apenas uma classe de utilidade. Se feito corretamente, não deve haver 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 faça singletons, não há absolutamente nada de errado nisso. Você mencionou uma classe de controlador, e isso quase certamente exigirá alterações de estado; portanto, desaconselho o uso apenas de métodos estáticos. Você pode confiar fortemente em uma classe de utilitário estático, mas não pode torná- lo uma classe de utilitário estático.
O que é considerado uma mudança de estado para uma classe? 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 frequentemente.
Uma função pura é aquela que é determinística, ou seja, para uma determinada entrada, você obterá uma e exatamente uma saída. Você deseja que 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 assume imediatamente que é uma função pura. Desviar-se do comportamento esperado é como você tende a criar bugs no seu programa, geralmente falando e deve ser evitado.
Um singleton é uma classe que contém métodos estáticos tão opostos à "função pura" quanto possível. 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:
fonte
Dice.roll()
não é uma exceção válida à regra "nenhum estado global".random.nextInt(6) + 1
. ;)RollAndMove()
rola novamente quando fornecemos um seis duplo, a maneira mais fácil e robusta de fazer isso é zombar de umDice
ou de um gerador de números aleatórios. Portanto,Dice
não deseja ser uma classe estática usando um gerador aleatório estático.Para dar um exemplo das limitações de uma
static
classe, e se alguns de seus jogadores quiserem receber 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 de D20s.
Dice.roll(bonus, sides)
Sim, mas alguns jogadores têm o talento "supremamente capaz" para que nunca possam "atrapalhar" (rolar 1).
Dice.roll(bonus, sides, isFumbleAllowed)
.Isso está ficando confuso, não é?
fonte
new Dice().roll(...)
deve ser considerado acesso estático e tambémDice.roll(...)
. Só nos beneficiamos se injetarmos essa dependência.static
confusão ocorre em todas as chamadas e o conhecimento necessário é necessário em todas as chamadas, para que seja espalhado globalmente em toda a sua aplicação. Em uma estrutura OOP, apenas o construtor / fábrica precisa ser confuso e os detalhes desse dado são encapsulados. Depois disso, use polimorfismo e apenas ligueroll()
. Eu posso editar esta resposta para esclarecer.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ê deseja testar uma unidade de algo que usa uma instância de Dados (por exemplo, uma classe de Jogo), a partir de seus testes, você pode injetar alguma forma de teste dupla de Dados que sempre retorna uma sequência fixa de valores. Seu teste pode verificar se o jogo tem o resultado certo para essas jogadas de dados.
Não sou desenvolvedor de Java, mas acho que seria significativamente mais difícil fazer isso com uma classe Dice totalmente estática. Consulte /programming/4482315/why-does-mockito-not-mock-static-methods
fonte
Isso é realmente conhecido como o padrão Monostate , em que cada instância (e até uma "não instância") está compartilhando seu status. Todo membro é um membro da classe (ou seja, nenhum membro da instância). Eles são comumente usados para implementar classes "toolkit" 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). De fato, o Java vem com alguns deles empacotados (por exemplo, Math ).
Um pouco fora do tópico: 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 própria classe e todas as suas instâncias) do questatic
(permanece após o ciclo de vida do escopo em que está declarado).fonte