Se as classes “ResourceManager” são consideradas ruins, quais são as alternativas?


19

Estou ouvindo opiniões conflitantes, como:

  • "As aulas de gerente dedicado quase nunca são a ferramenta de engenharia certa"
  • "As aulas de gerente dedicado são (atualmente) a melhor maneira de sobreviver a um grande projeto com milhares de recursos"

Vamos dar uma classe clássica do ResourceManager que possui a seguinte funcionalidade:

  • Carrega ativos (texturas, áudio, modelos 3D etc.)
  • Garante que os ativos sejam carregados apenas uma vez, mantendo um cache
  • A referência conta os ativos para determinar se eles podem ser desalocados
  • Oculta a origem dos ativos reais (por exemplo, pode haver um arquivo por ativo ou todos os ativos em um arquivo de pacote ou os ativos podem até ser carregados pela rede)
  • Pode recarregar ativos sem reiniciar o programa, o que é extremamente útil para os artistas que trabalham no jogo.

Vamos também tirar o argumento "singletons ruins" da mesa , fingindo que esses objetos ResourceManager não são singletons e, em vez disso, são transmitidos por injeção de dependência .

Depois, há o argumento "use a factory" ou "chame de fábrica". Meu problema com isso é que, sim, é uma fábrica, mas também é um cache e um recarregador (por falta de uma palavra melhor). Chamar isso de fábrica não o descreve adequadamente e, se eu o tornar uma fábrica adequada, onde é implementado o cache e o recarregamento?

Concordo que as classes "Gerente" geralmente são um sintoma de arquitetura ruim, mas nesse caso específico, como ela poderia ser reestruturada e ainda manter toda a funcionalidade ? É uma situação em que uma classe "Gerente" é realmente apropriada?


3
Talvez chamá-lo de armazém em vez de fábrica? : P
deceleratedcaviar

Respostas:


9

Eu acho que a questão não é que este seja um design ruim. O problema é que o nome "Gerente" é indefinido, como "Atualização" e "Processo" e "Ator". É inútil para alguém tentar entender esse sistema e cria confusão se você tiver outras classes que executam alguma operação nos ativos.

No entanto, nomear as coisas de uma maneira que transmita seu objetivo com eficiência é muito, muito difícil . A maioria dos sistemas é muito complicada para se tornar óbvia pelo nome. O melhor que você pode fazer é tentar restringir que ideia específica torna essa classe coesa, e que outras classes precisam ser obviamente diferentes e escolher uma palavra única que se encaixe.

IMHO, a característica mais importante de um bom nome é que ele é único no contexto em que as pessoas o veem (a base de código), e é fácil de lembrar. O teste deve ser o de que você pode conversar com seus colegas de equipe que estão familiarizados com o código sem precisar esclarecer a que você está se referindo. Isso também ajudaria se você precisar escrever uma documentação de referência.

Por fim, considere que, se você não pode descrever essa ideia coesa, ela pode não ser muito coesa. Por exemplo, você pode separar a contagem de cache e de referência em um AssetCache e manter o carregamento em um AssetLoader. Mas isso realmente depende de quão complicado e entrelaçado o código é.

Mas se o seu AssetManager for suficientemente coeso e nada mais no seu jogo for AssetManagery, então pode ser um nome perfeitamente bom.


A separação AssetLoader / AssetCache não é uma má ideia.
Tom Dalling

4

"Faço isso em um só lugar ou faço muitos?"

A lógica de carregamento geralmente é centralizada em um único ponto de acesso, porque tende a ser executada apenas em pontos-chave do aplicativo, onde estamos tentando tirá-lo do caminho o mais rápido possível. Isso geralmente ocorre na inicialização do aplicativo, no nível do jogo ou quando a posição do mundo do jogador exige o carregamento de um novo bloco para manter a experiência perfeita. Considerando que isso é feito como um processo em lote (seja em um pedaço maciço ou em muitos menores, realmente não importa), faz sentido ter um método ou conjunto de métodos para chamar, para iniciar esse processo.

Na maioria das plataformas, o carregamento é assíncrono; portanto, você tem poucas opções, exceto manter os fluxos de código para carregamento e outra lógica de aplicativo separados, outro motivo para abstração das funções de gerenciamento de recursos em sua própria classe.

Por fim, alguns pontos a serem considerados:

O carregamento de recursos não é executado com muita frequência em comparação com, por exemplo. lógica do jogo, vale a pena adicionar complexidade a seus objetos de jogo individuais para fazer isso? Os objetos do jogo geralmente são mantidos leves o máximo possível e são destinados apenas ao necessário durante a simulação.

Um objeto deve ser responsável por sua própria criação e destruição? Isso pode causar grandes dores de cabeça durante o gerenciamento da entidade. Se você espera que um objeto execute seu próprio processo de criação (incluindo a obtenção de dependências de recursos), também terá a responsabilidade de se destruir. Isso pode causar ponteiros pendentes / nulos; portanto, ele precisa ser gerenciado de fora. Veja esta resposta descrevendo esse problema com mais detalhes.


PS Além dos argumentos usuais do tipo "Singletons são terríveis" (que são terrivelmente dogmáticos), você já viu um bom argumento contra os ResourceManagers? "Classes de gerente dedicado quase nunca são a ferramenta de engenharia certa" não é argumento para este caso específico .

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.