É uma má idéia ter Game1 estático no XNA?

10

É uma péssima idéia ter minha Game1classe como estática? Como no momento da minha Game1aula, tenho uma classe chamada TileHandlerque lida com tudo que tem a ver com o meu conjunto atual de peças e AnimalHandlerque lida com todos os meus animais (surpreendentemente).

Agora, se eu estiver dentro AnimalHandlere quiser verificar se um bloco pode ser percorrido a partir de TileHandlerentão, isso causa problemas ou eu tenho que passar uma lista de blocos que podem ser percorridos AnimalHandler, o que eu prefiro não fazer.

O que seria mais fácil seria tornar Game1estático e depois AnimalHandlerir embora Game1._tileHandler.WalkableTile(position).

Agora não consigo ver nada imediatamente errado com isso ou com qualquer coisa que possa causar problemas, mas apenas comecei a usar classes estáticas. Alguém mais experiente vê alguma razão gigante por que essa é uma má ideia?

Harold
fonte

Respostas:

9

Agora não consigo ver nada imediatamente errado com isso ou com qualquer coisa que possa causar problemas, mas apenas comecei a usar classes estáticas. Alguém mais experiente vê alguma razão gigante por que essa é uma má ideia?

Quando você tem um novo martelo brilhante, todo problema parece um prego.

Em geral, não há nada de errado com classes e / ou métodos estáticos, se usados ​​corretamente (para itens que não possuem ou dependem do estado por instância). No entanto, no seu caso, você os está usando incorretamente para ocultar uma dependência por instância, confundindo isso com a remoção da dependência. Parece também que você está expondo os detalhes de implementação da Game1classe, o que geralmente também é ruim.

Aqui está o ponto crucial do seu problema:

... se eu estiver dentro AnimalHandlere quiser verificar se um bloco pode ser percorrido a partir de TileHandlerentão, isso causa problemas ou eu tenho que passar uma lista de blocos que podem ser percorridos AnimalHandler, o que eu prefiro não fazer.

Ignorando a possibilidade de que AnimalHandlerprecisar desses blocos possa ser um projeto ruim (com os nomes que você escolheu, é difícil contar os detalhes dessas classes) no momento ... se AnimalHandlerprecisar de uma lista de blocos acessíveis, então precisará de uma lista de telhas tranquilas. Geralmente, é melhor tornar as dependências mais explícitas do que menos , pois isso torna o código mais auto-documentado. Ao passar a lista diretamente para AnimalHandler, você explicita o fato de que ela precisa dessa lista. Se você tornar tudo estático e público para acessar a lista estática mantida em outra parte do código, tudo o que você faz é ocultar a dependência sem realmente resolvê-la ou removê-la.

Para um jogo pequeno que não precisa ser escalado, isso não será um problema, por si só, mas pode levar você a seguir um mau hábito, portanto, considere não fazê-lo. No mínimo, lembre-se disso para o próximo projeto em que você trabalha.


fonte
Amém! Você toca em um ótimo ponto sobre o qual eu realmente não falei na minha resposta.
Nate
Esconder as dependências com +1 e introduzir o estado global é realmente o problema aqui
ashes999
4

A razão pela qual chamar o TileHandler em um contexto estático não é o melhor design possível, é o fato de associar componentes do seu design que poderiam ser dissociados.

Se você optar por ter mais de um TileHandler no futuro, precisará fazer muito trabalho para acomodar essa alteração.

Se você optar por remover o TileHandler, precisará fazer muito trabalho para acomodar essa alteração.

Suponha que você construa um nível / zona diferente no futuro, que lide com blocos de maneira diferente do seu TileHandler atual. Então você precisa ter uma maneira de especificar o método de manipulação de lado a lado a ser usado ou precisa chamar um manipulador diferente.

Se o TileHandler foi passado como um parâmetro para objetos que o utilizam, você pode simplesmente passar um diferente na próxima vez ou definir um manipulador de mosaico diferente nos objetos que o usam posteriormente.

Pessoalmente, acesso muitas coisas nos meus jogos XNA a partir de um contexto estático e presumo que nunca terei mais que um deles.

Se você quiser reutilizar o código do mecanismo de jogo no próximo jogo, provavelmente precisará reescrever muitas das coisas que você escreveu atualmente como estáticas.

Em resumo:

A favor de não usar o contexto estático:

Passar objetos como parâmetros, tanto quanto possível, dissocia elementos do jogo e permite modificá-los / reutilizá-los para os projetos atuais ou futuros com mais facilidade. Ele também permite que você gerencie a complexidade de grandes quantidades de código um pouco mais fácil (pense em ter centenas de gerenciadores estáticos em sua classe de jogo, em um grande jogo).

A favor do contexto estático:

Declarar e acessar objetos de um contexto estático facilita a criação de pequenos jogos que não exigem centenas de gerenciadores de estática. Simplifica muitos métodos e construtores, não exigindo um ou mais parâmetros extras que são acessados ​​estaticamente.

Olhovsky
fonte
2

Não acho que seja uma má ideia para um jogo simples, mas você também pode dar uma olhada em http://www.nuclex.org/articles/4-architecture/6-game-components-and-game-services para uma idéia melhor de como criar componentes de jogos intercomunicantes

smnbss
fonte
Você poderia pensar em algum problema se não fosse apenas um jogo simples?
Harold
Legibilidade do código, testabilidade e boas práticas de arquitetura sugerem evitá-lo.
smnbss 04/04
2

Coisas como TileHandler e AnimalHandler Eu colocaria um nível mais alto na tela de um jogo. Sua tela de título precisa de acesso ao TileHandler e é inicializada quando o jogo é carregado pela primeira vez? Provavelmente não.

Confira a amostra XNA State Management . Tem muito código lá, mas basicamente o jogo base apenas inicializa uma pilha de estados (ou telas) do jogo. Cada tela é bastante independente das outras e funciona como uma versão simplificada do próprio jogo. Seu PlayScreen pode ter membros estáticos, para que eles sejam acessíveis aos componentes do PlayScreen.

No jogo base, eu uso algumas estatísticas, mas são coisas de nível muito baixo, como os leitores InputHelper, Log ou Config. Eles são bastante comuns em todos os jogos, para que o mecanismo básico possa ser transportado rápida e facilmente. As telas são onde a lógica real do jogo acontece. Resposta longa por muito tempo - não, não acho que seja uma má idéia em teoria, apenas tome cuidado com o que você faz estática. Depois que você faz algo estático, é uma tremenda quantidade de trabalho se você mudar de idéia.

Clemência
fonte
0

Os pontos levantados aqui são todos bons. Na minha experiência (que reconhecidamente é mais voltada para aplicativos de negócios do que jogos), há ótimos usos para membros e classes estáticas e eu os usei muitas vezes. Descobri que, à medida que os requisitos e a complexidade aumentam, acabo reciclando essas classes estáticas e convertendo-as em classes de instância e começo a distribuí-las.

O ponto que eu gostaria de destacar é que, se o uso de uma classe estática o ajudar a finalizar o jogo, siga em frente, mas faça as coisas certas: implemente uma interface ou classe base para que seja mais fácil extraí-lo e convertê-lo para uma classe de instância mais tarde.

Um grama de prevenção vale um quilo de cura, portanto, verifique se a classe estática não o prende, o que dificulta a mudança. É muito fácil refatorar um método usando uma classe estática que implementa uma interface, para que ele aceite um novo parâmetro de interface e use essa interface em vez da referência de classe estática.

Nate
fonte
0

Sim, geralmente é sempre uma má ideia.

Transformar objetos que contêm dados alterados (ou seja, qualquer coisa além de constantes somente leitura e tabelas de pesquisa) em classes estáticas é onde o bom design entra em ação.

  1. Promove dependências aleatórias que eliminam a modularidade e atrapalham a reutilização de código. Suponha que você queira escrever um editor para o seu jogo - de repente você terá muitas aulas chorando por umGame1 que não possa ser movida facilmente para uma biblioteca compartilhada.

  2. Seu código se torna não testável. O teste de unidade funciona isolando classes individuais das demais, zombando ou simulando suas dependências (que devem ser mantidas o mínimo possível). Qualquer classe estática é uma responsabilidade, pois pode ser acessada a qualquer momento, transportar o estado em vários testes ou exigir que seja inicializada antes que os testes possam ter êxito.

  3. Oculta dependências. Assim como o antipadrão do provedor de serviços (também empregado pelo XNA, Game.Services), suas classes podem escolher dependências que você não vê externamente.

Essa abordagem se torna especialmente venenosa se alguém combinar classes estáticas com chamadas de trem de carga (coisas como é o que ocorre Game.Instance.ActorManager.Enemies.FindInRange(10)por causa dos símbolos encadeados em vagões), onde componentes repentinamente não apenas exigem uma Gameclasse, mas uma com uma Instancepropriedade estática que retorna algo com uma ActorManagerpropriedade que tem umEnemies propriedade que retorna um objeto com um FindInRange()método

A única desculpa que eu aceitaria para escrever classes estáticas mutáveis ​​seria a que ainda está aprendendo e ainda não tem a capacidade de aplicar consistentemente um bom design e um olho treinado para detectar más escolhas ainda.

Cygon
fonte