Soy un aspirante a desarrollador de juegos, trabajo en juegos independientes ocasionales, y durante un tiempo he estado haciendo algo que parecía una mala práctica al principio, pero realmente quiero obtener una respuesta de algunos programadores experimentados aquí.
Digamos que tengo un archivo llamado enumList.h
donde declaro todas las enumeraciones que quiero usar en mi juego:
// 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
};
La idea principal es declarar todas las enumeraciones en el juego en 1 archivo , y luego importar ese archivo cuando necesito usar una cierta enumeración de él, en lugar de declararlo en el archivo donde necesito usarlo. Hago esto porque hace las cosas limpias, puedo acceder a cada enumeración en 1 lugar en lugar de tener páginas abiertas solo para acceder a una enumeración.
¿Es esta una mala práctica y puede afectar el rendimiento de alguna manera?
fuente
materials_t
archivos que no se ocupan de materiales, deberá reconstruirse.enumList.h
sirviendo como una colección de#include
s para esos archivos. Esto permite que los archivos que solo necesitan una enumeración lo obtengan directamente, al tiempo que proporciona un paquete singular para todo lo que realmente los quiere a todos.Respuestas:
Realmente creo que es una mala práctica. Cuando medimos la calidad del código, hay algo que llamamos "granularidad". Su granularidad sufre severamente al poner todas esas enumeraciones en un solo archivo y, por lo tanto, la mantenibilidad también sufre.
Tendría un solo archivo por enumeración para encontrarlo rápidamente y agruparlo con el código de comportamiento de la funcionalidad específica (por ejemplo, enumeración de materiales en la carpeta donde está el comportamiento del material, etc.);
Puede pensar que está limpio, pero de hecho, no lo está. Acopla cosas que no se unen entre sí en cuanto a funcionalidad y módulo, y disminuye la modularidad de su aplicación. Dependiendo del tamaño de su base de código y de qué tan modular quiere que se estructura su código, esto podría convertirse en un problema mayor y en código / dependencias sucias en otras partes de su sistema. Sin embargo, si solo escribe un sistema pequeño y monolítico, esto no se aplica necesariamente. Sin embargo, no lo haría así incluso para un pequeño sistema monolítico.
fuente
Sí, es una mala práctica, no por el rendimiento sino por la capacidad de mantenimiento.
Hace que las cosas estén "limpias" solo en el modo OCD de "juntar cosas similares". Pero ese no es el tipo de "limpieza" útil y bueno.
Las entidades de código deben agruparse para maximizar la cohesión y minimizar el acoplamiento , lo que se logra mejor al agruparlas en módulos funcionales en gran medida independientes. Agruparlos por criterios técnicos (como juntar todas las enumeraciones) logra lo contrario: combina el código que no tiene absolutamente ninguna relación funcional y coloca enumeraciones que podrían usarse solo en un lugar en un archivo diferente.
fuente
Bueno, esto son solo datos (no comportamiento). Lo peor que podría suceder posible / teóricamente es que se incluya y compile el mismo código más de una vez produciendo un programa relativamente más grande.
Es simplemente imposible que tales inclusiones puedan agregar más ciclos a sus operaciones, dado que no hay ningún código de comportamiento / procedimiento dentro de ellas (sin bucles, sin ifs, etc.).
Un programa (relativamente) más grande difícilmente puede afectar el rendimiento (velocidad de ejecución) y, en cualquier caso, es solo una preocupación teórica remota. La mayoría (tal vez todos) los compiladores administran incluyen de una manera que previene tales problemas.
En mi humilde opinión, las ventajas que obtienes con un solo archivo (código más legible y más manejable) superan en gran medida cualquier posible inconveniente.
fuente
Para mí, todo depende del alcance de su proyecto. Si hay un archivo con digamos 10 structs y esos son los únicos que se han usado, me sentiría perfectamente cómodo teniendo un archivo .h para él. Si tiene varios tipos diferentes de funcionalidad, digamos unidades, economía, edificios, etc. Definitivamente dividiría las cosas. Cree un units.h donde residen todas las estructuras que tratan con unidades. Si desea hacer algo con unidades en algún lugar, debe incluir unidades. Claro, pero también es un buen "identificador" de que algo con unidades probablemente se haga en este archivo.
Míralo de esta manera, no compras un supermercado porque necesitas una lata de coca cola;)
fuente
No soy un desarrollador de C ++, por lo que esta respuesta será más genérica para OOA & D:
Típicamente, los objetos de código deben agruparse en términos de relevancia funcional, no necesariamente en términos de construcciones específicas del lenguaje. La pregunta clave sí-no que siempre se debe hacer es: "¿se espera que el codificador final use la mayoría o la totalidad de los objetos que obtiene al consumir una biblioteca?" En caso afirmativo, grupo de distancia. De lo contrario, considere dividir los objetos de código y colocarlos más cerca de otros objetos que los necesitan (aumentando así la posibilidad de que el consumidor necesite todo lo que realmente está accediendo).
El concepto básico es "alta cohesión"; los miembros del código (desde los métodos de una clase hasta las clases en un espacio de nombres o DLL, y las propias DLL) deben organizarse de manera que un codificador pueda incluir todo lo que necesitan y nada que no. Esto hace que el diseño general sea más tolerante al cambio; las cosas que deben cambiar pueden ser, sin afectar a otros objetos de código que no tuvieron que cambiar. En muchas circunstancias, también hace que la aplicación sea más eficiente en memoria; una DLL se carga en su totalidad en la memoria, independientemente de la cantidad de esas instrucciones que el proceso ejecute alguna vez. Por lo tanto, diseñar aplicaciones "esbeltas" requiere prestar atención a la cantidad de código que se extrae en la memoria.
Este concepto se aplica a prácticamente todos los niveles de organización del código, con diversos grados de impacto en la capacidad de mantenimiento, la eficiencia de la memoria, el rendimiento, la velocidad de creación, etc. Si un codificador necesita hacer referencia a un encabezado / DLL de tamaño bastante monolítico solo para acceder a un objeto, que no depende de ningún otro objeto en esa DLL, entonces la inclusión de ese objeto en la DLL probablemente debería repensarse. Sin embargo, también es posible ir demasiado lejos en la otra dirección; Una DLL para cada clase es una mala idea, ya que reduce la velocidad de compilación (más DLL para reconstruir con la sobrecarga asociada) y hace que el versionado sea una pesadilla.
Caso en cuestión: si cualquier uso del mundo real de su biblioteca de códigos implicaría usar la mayoría o todas las enumeraciones que está colocando en este único archivo "enumerations.h", entonces agrúpelas por todos los medios; sabrás dónde buscar para encontrarlos. Pero si su codificador consumidor pudiera necesitar solo una o dos de las docenas de enumeraciones que está proporcionando en el encabezado, entonces le animo a que las coloque en una biblioteca separada y la convierta en una dependencia de la más grande con el resto de las enumeraciones. . Eso permite a sus codificadores obtener solo el uno o dos que desean sin vincular a la DLL más monolítica.
fuente
Este tipo de cosas se convierte en un problema cuando tienes múltiples desarrolladores trabajando en la misma base de código.
Ciertamente, he visto archivos globales similares que se convierten en un nexo para las colisiones de fusión y todo tipo de dolor en proyectos (más grandes).
Sin embargo, si usted es el único que trabaja en el proyecto, entonces debe hacer lo que le resulte más cómodo y solo adoptar las "mejores prácticas" si comprende (y está de acuerdo) con la motivación detrás de ellos.
Es mejor cometer algunos errores y aprender de ellos que arriesgarse a quedarse atrapado con las prácticas de programación de culto de carga por el resto de su vida.
fuente
Sí, es una mala práctica hacerlo en un gran proyecto. BESO.
El joven compañero de trabajo cambió el nombre de una variable simple en un archivo .h central y 100 ingenieros esperaron 45 minutos para que se reconstruyeran todos sus archivos, lo que afectó el rendimiento de todos. ;)
Todos los proyectos comienzan pequeños, con el paso de los años, y maldecimos a quienes tomaron atajos tempranos para crear deuda técnica. Mejores prácticas, mantenga el contenido global .h limitado a lo que es necesariamente global.
fuente
En este caso, diría que la respuesta ideal es que depende de cómo se consuman las enumeraciones, pero que en la mayoría de las circunstancias probablemente sea mejor definir todas las enumeraciones por separado, pero si alguna de ellas ya está unida por diseño, debe proporcionar un medios de introducir dichas enumeraciones acopladas colectivamente. En efecto, tiene una tolerancia de acoplamiento hasta la cantidad de acoplamiento intencional ya presente, pero no más.
Teniendo esto en cuenta, es probable que la solución más flexible defina cada enumeración en un archivo separado, pero proporcione paquetes acoplados cuando sea razonable hacerlo (según lo determine el uso previsto de las enumeraciones involucradas).
La definición de todas sus enumeraciones en el mismo archivo las une y, por extensión, hace que cualquier código que dependa de una o más enumeraciones dependa de todas las enumeraciones, independientemente de si el código realmente utiliza alguna otra enumeración.
renderMap()
preferiría saberlomap_t
, porque de lo contrario, cualquier cambio en los demás lo afectará aunque en realidad no interactúe con los demás.Sin embargo, en el caso de que los componentes ya estén acoplados entre sí, proporcionar múltiples enumeraciones en un solo paquete puede proporcionar claridad y simplicidad adicionales, siempre que haya una razón lógica clara para que las enumeraciones se acoplen, que el uso de esas enumeraciones también se acople, y que proporcionarlos tampoco introduce acoplamientos adicionales.
En este caso, no existe una conexión directa y lógica entre el tipo de entidad y el tipo de material (suponiendo que las entidades no estén hechas de uno de los materiales definidos). Sin embargo, si tenemos un caso en el que, por ejemplo, una enumeración depende explícitamente de la otra, entonces tiene sentido proporcionar un solo paquete que contenga todas las enumeraciones acopladas (así como cualquier otro componente acoplado), de modo que el acoplamiento pueda ser aislado de ese paquete tanto como sea razonablemente posible.
Pero, por desgracia ... incluso cuando dos enumeraciones están intrínsecamente juntas, incluso si es algo tan fuerte como "la segunda enumeración proporciona subcategorías para la primera enumeración", todavía puede llegar un momento en que solo una de las enumeraciones sea necesaria.
Por lo tanto, dado que sabemos que siempre puede haber situaciones en las que definir enumeraciones múltiples en un solo archivo puede agregar un acoplamiento innecesario, y que proporcionar enumeraciones acopladas en un solo paquete puede aclarar el uso previsto y permitirnos aislar el código de acoplamiento real como En la medida de lo posible, la solución ideal es definir cada enumeración por separado y proporcionar paquetes conjuntos para cualquier enumeración que se pretenda utilizar con frecuencia. Las únicas enumeraciones definidas en el mismo archivo serán las que están intrínsecamente vinculadas entre sí, de modo que el uso de uno requiere el uso del otro también.
fuente