Eu sou um desenvolvedor de jogos aspirante, trabalho ocasionalmente em jogos independentes e, durante algum tempo, venho fazendo algo que parecia uma prática ruim no começo, mas eu realmente quero receber uma resposta de alguns programadores experientes aqui.
Digamos que eu tenho um arquivo chamado enumList.h
onde declaro todas as enumerações que quero usar no meu jogo:
// enumList.h
enum materials_t { WOOD, STONE, ETC };
enum entity_t { PLAYER, MONSTER };
enum map_t { 2D, 3D };
// and so on.
// Tile.h
#include "enumList.h"
#include <vector>
class tile
{
// stuff
};
A idéia principal é que declaro todas as enumerações do jogo em 1 arquivo e depois importo esse arquivo quando preciso usar uma determinada enumeração, em vez de declará-lo no arquivo em que preciso usá-lo. Eu faço isso porque isso torna as coisas limpas; eu posso acessar todas as enum em um lugar, em vez de ter páginas abertas apenas para acessar uma enum.
Essa é uma prática ruim e pode afetar o desempenho de alguma forma?
fonte
materials_t
arquivos que não tratam de materiais, precisará ser reconstruído.enumList.h
servindo como uma coleção de#include
s para esses arquivos. Isso permite que os arquivos que precisam apenas de uma enumeração o obtenham diretamente, enquanto fornecem um pacote singular para qualquer coisa que realmente queira todos eles.Respostas:
Eu realmente acho que é uma má prática. Quando medimos a qualidade do código, há algo que chamamos de "granularidade". Sua granularidade sofre severamente ao colocar todas essas enumerações em um único arquivo e, portanto, também sofre manutenção.
Eu teria um único arquivo por enum para encontrá-lo rapidamente e agrupá-lo com o código comportamental da funcionalidade específica (por exemplo, enum de materiais na pasta onde o comportamento dos materiais é etc.);
Você pode pensar que é limpo, mas na verdade não é. Ele está acoplando itens que não pertencem juntos à funcionalidade e ao módulo e diminui a modularidade do seu aplicativo. Dependendo do tamanho da sua base de códigos e da modularidade que você deseja que seu código seja estruturado, isso pode evoluir para um problema maior e para códigos / dependências impuros em outras partes do seu sistema. No entanto, se você acabou de escrever um pequeno sistema monolítico, isso não se aplica necessariamente. No entanto, eu não faria isso mesmo em um pequeno sistema monolítico.
fonte
Sim, é uma prática ruim, não por causa do desempenho, mas por causa da capacidade de manutenção.
Isso torna as coisas "limpas" apenas no TOC "coleciona coisas similares". Mas esse não é realmente o tipo útil e bom de "limpeza".
As entidades de código devem ser agrupadas para maximizar a coesão e minimizar o acoplamento , o que é melhor alcançado agrupando-as em módulos funcionais amplamente independentes. Agrupá-los por critérios técnicos (como reunir todas as enumerações) alcança o oposto - ele combina código que não tem absolutamente nenhuma relação funcional e coloca enumerações que podem ser usadas apenas em um local em um arquivo diferente.
fonte
Bem, isso são apenas dados (não comportamento). O pior que poderia / teoricamente acontecer é que o mesmo código seja incluído e compilado mais de uma vez produzindo um programa relativamente maior.
É simplesmente impossível que tais inclusões possam adicionar mais ciclos às suas operações, uma vez que não há nenhum código comportamental / processual dentro delas (sem loops, sem ifs etc.).
Um programa (relativamente) maior dificilmente pode afetar o desempenho (velocidade de execução) e, em qualquer caso, é apenas uma preocupação teórica remota. A maioria (talvez todos) dos compiladores gerenciam inclui de uma maneira que evite esses problemas.
IMHO, as vantagens que você obtém com um único arquivo (código mais legível e mais gerenciável) excedem em grande parte qualquer possível inconveniente.
fonte
Para mim, tudo depende do escopo do seu projeto. Se houver um arquivo com, digamos, 10 estruturas e os únicos já usados, eu ficaria perfeitamente confortável em ter um arquivo .h. Se você tem vários tipos diferentes de funcionalidade, por exemplo, unidades, economia, edifícios, etc. Eu definitivamente dividiria as coisas. Crie um units.h onde residem todas as estruturas que lidam com as unidades. Se você quiser fazer algo com unidades em algum lugar, precisará incluir units.h, com certeza, mas também é um bom "identificador" que algo com unidades provavelmente seja feito neste arquivo.
Veja desta forma, você não compra um supermercado porque precisa de uma lata de coca-cola;)
fonte
Como eu não sou um desenvolvedor de C ++, esta resposta será mais genérica para OOA & D:
Normalmente, os objetos de código devem ser agrupados em termos de relevância funcional, não necessariamente em termos de construções específicas da linguagem. A principal pergunta sim-não que sempre deve ser feita é: "será esperado que o codificador final use a maioria ou todos os objetos que obtém ao consumir uma biblioteca?" Se sim, agrupe-se. Caso contrário, considere dividir os objetos de código e colocá-los mais próximos de outros objetos que precisam deles (aumentando assim a chance de o consumidor precisar de tudo o que realmente está acessando).
O conceito básico é "alta coesão"; os membros do código (dos métodos de uma classe até as classes em um namespace ou DLL e as próprias DLLs) devem ser organizados de forma que um codificador possa incluir tudo o que precisa e nada que não seja. Isso torna o design geral mais tolerante a mudanças; coisas que precisam mudar podem ser, sem afetar outros objetos de código que não precisavam ser alterados. Em muitas circunstâncias, também torna o aplicativo mais eficiente em termos de memória; uma DLL é carregada na sua totalidade na memória, independentemente de quanto dessas instruções já são executadas pelo processo. A criação de aplicativos "enxutos" exige, portanto, que se preste atenção à quantidade de código extraída na memória.
Esse conceito se aplica a praticamente todos os níveis da organização do código, com diferentes graus de impacto na capacidade de manutenção, eficiência da memória, desempenho, velocidade de compilação etc. Se um codificador precisar fazer referência a um cabeçalho / DLL de tamanho bastante monolítico apenas para acessar um objeto, que não depende de nenhum outro objeto nessa DLL, a inclusão desse objeto na DLL provavelmente deve ser repensada. No entanto, é possível ir longe demais também; uma DLL para todas as classes é uma má ideia, pois diminui a velocidade de criação (mais DLLs são reconstruídas com sobrecarga associada) e torna o versionamento um pesadelo.
Caso em questão: se qualquer uso do mundo real da sua biblioteca de códigos envolver o uso da maioria ou de todas as enumerações que você está colocando nesse único arquivo "enumerations.h", então, por todos os meios, agrupe-as; você saberá onde procurar para encontrá-los. Mas se seu codificador consumidor puder precisar de apenas uma ou duas das dezenas de enumerações que você está fornecendo no cabeçalho, recomendamos que você as coloque em uma biblioteca separada e faça com que seja uma dependência da maior com o restante das enumerações . Isso permite que seus codificadores obtenham apenas o um ou dois que desejam, sem vincular à DLL mais monolítica.
fonte
Esse tipo de coisa se torna um problema quando você tem vários desenvolvedores trabalhando na mesma base de código.
Certamente vi arquivos globais semelhantes se tornando um nexo para colisões de mesclagem e todo tipo de sofrimento em projetos (maiores).
No entanto, se você é o único que trabalha no projeto, deve fazer o que estiver mais à vontade e adotar as "melhores práticas" se entender (e concordar com) a motivação por trás deles.
É melhor cometer alguns erros e aprender com eles do que correr o risco de ficar preso às práticas de programação de cultos de carga pelo resto da sua vida.
fonte
Sim, é uma prática ruim fazer isso em um grande projeto. BEIJO.
O colega de trabalho jovem renomeou uma variável simples em um arquivo .he principal e 100 engenheiros esperaram 45 minutos para que todos os seus arquivos fossem reconstruídos, o que afetou o desempenho de todos. ;)
Todos os projetos começam pequenos, com o passar dos anos, e amaldiçoamos aqueles que adotaram atalhos iniciais para criar dívida técnica. Práticas recomendadas, mantenha o conteúdo global .h limitado ao que é necessariamente global.
fonte
Nesse caso, eu diria que a resposta ideal é que depende de como as enumerações são consumidas, mas na maioria das circunstâncias provavelmente é melhor definir todas as enumerações separadamente, mas se alguma delas já estiver acoplada pelo design, você deverá fornecer um meios de introduzir as referidas enumerações acopladas coletivamente. Com efeito, você tem uma tolerância de acoplamento até a quantidade de acoplamento intencional já presente, mas não mais.
Considerando isso, é provável que a solução mais flexível defina cada enum em um arquivo separado, mas forneça pacotes acoplados quando for razoável fazê-lo (conforme determinado pelo uso pretendido das enumerações envolvidas).
Definir todas as suas enumerações no mesmo arquivo as une e, por extensão, faz com que qualquer código que depende de uma ou mais enumerações dependa de todas as enumerações, independentemente de o código realmente usar outras enumerações.
renderMap()
preferiria apenas saber sobre issomap_t
, porque, caso contrário, qualquer alteração nos outros o afetará, mesmo que na verdade não interaja com os outros.No entanto, no caso em que os componentes já estão acoplados, o fornecimento de várias enumerações em um único pacote pode facilmente fornecer clareza e simplicidade adicionais, desde que haja uma razão lógica clara para que as enumerações sejam acopladas, que o uso dessas enumerações também seja acoplado, e que fornecê-los também não introduz acoplamentos adicionais.
Nesse caso, não há conexão lógica direta entre o tipo de entidade e o tipo de material (supondo que as entidades não sejam feitas de um dos materiais definidos). Se, no entanto, tivermos um caso em que, por exemplo, um enum seja explicitamente dependente do outro, faz sentido fornecer um único pacote contendo todas as enumerações acopladas (assim como quaisquer outros componentes acoplados), para que o acoplamento possa ser isolado para esse pacote, tanto quanto for razoavelmente possível.
Mas, infelizmente ... mesmo quando duas enums estão intrinsecamente acopladas, mesmo que seja algo tão forte quanto "o segundo enum fornece subcategorias para o primeiro enum", ainda pode chegar um momento em que apenas um dos enums é necessário.
Portanto, como sabemos que sempre pode haver situações em que a definição de várias enumerações em um único arquivo pode adicionar acoplamento desnecessário e que o fornecimento de enumerações em um único pacote pode esclarecer o uso pretendido e nos permitir isolar o próprio código de acoplamento como Na medida do possível, a solução ideal é definir cada enumeração separadamente e fornecer pacotes conjuntos para quaisquer enumerações que se destinam a serem frequentemente usadas juntas. As únicas enumerações definidas no mesmo arquivo serão aquelas intrinsecamente ligadas, de modo que o uso de um também exija o uso do outro.
fonte