¿Dónde deberías poner constantes y por qué?

34

En nuestras aplicaciones en su mayoría grandes, generalmente tenemos unas pocas ubicaciones para "constantes":

  • Una clase para GUI y constantes internas (títulos de página de pestaña, títulos de cuadro de grupo, factores de cálculo, enumeraciones)
  • Una clase para tablas y columnas de bases de datos (esta parte es código generado) más nombres legibles para ellas (asignadas manualmente)
  • Una clase para mensajes de aplicación (registro, cuadros de mensaje, etc.)

Las constantes generalmente se separan en diferentes estructuras en esas clases. En nuestras aplicaciones C ++, las constantes solo se definen en el archivo .h y los valores se asignan en el archivo .cpp.

Una de las ventajas es que todas las cadenas, etc., se encuentran en un lugar central y todos saben dónde encontrarlas cuando se debe cambiar algo.

Esto es especialmente algo que parece gustarle a los gerentes de proyecto a medida que las personas van y vienen y de esta manera todos pueden cambiar cosas tan triviales sin tener que profundizar en la estructura de la aplicación.

Además, puede cambiar fácilmente el título de cuadros de grupo / páginas de pestañas similares, etc. a la vez. Otro aspecto es que puede imprimir esa clase y dársela a un no programador que puede verificar si los subtítulos son intuitivos, y si los mensajes al usuario son demasiado detallados o confusos, etc.

Sin embargo, veo ciertas desventajas:

  • Cada clase está estrechamente unida a las clases constantes.
  • Agregar / Eliminar / Renombrar / Mover una constante requiere la recompilación de al menos el 90% de la aplicación (Nota: Cambiar el valor no lo hace, al menos para C ++). En uno de nuestros proyectos C ++ con 1500 clases, esto significa alrededor de 7 minutos de tiempo de compilación (usando encabezados precompilados; sin ellos son alrededor de 50 minutos) más alrededor de 10 minutos de vinculación con ciertas bibliotecas estáticas.
  • Crear una versión optimizada de velocidad a través del compilador de Visual Studio lleva hasta 3 horas. No sé si la gran cantidad de relaciones de clase es la fuente, pero también podría serlo.
  • Te conducen a cadenas de codificación temporal temporalmente directamente en el código porque quieres probar algo muy rápido y no quieres esperar 15 minutos solo para esa prueba (y probablemente cada una de las siguientes). Todo el mundo sabe lo que sucede con los pensamientos de "Lo arreglaré más tarde".
  • Reutilizar una clase en otro proyecto no siempre es tan fácil (principalmente debido a otros acoplamientos estrechos, pero el manejo de constantes no lo hace más fácil).

¿Dónde guardarías constantes así? Además, ¿qué argumentos presentaría para convencer a su gerente de proyecto de que existen mejores conceptos que también cumplen con las ventajas mencionadas anteriormente?

Siéntase libre de dar una respuesta específica o independiente de C ++.

PD: Sé que esta pregunta es un poco subjetiva, pero sinceramente, no conozco un lugar mejor que este sitio para este tipo de pregunta.

Actualización sobre este proyecto

Tengo noticias sobre el tiempo de compilación:
siguiendo las publicaciones de Caleb y gbjbaanb, dividí mi archivo de constantes en varios otros archivos cuando tuve tiempo. Finalmente, también dividí mi proyecto en varias bibliotecas, lo que ahora era mucho más fácil. Compilar esto en el modo de lanzamiento mostró que el archivo generado automáticamente que contiene las definiciones de la base de datos (tabla, nombres de columna y más, más de 8000 símbolos) y acumula ciertos hashes causó grandes tiempos de compilación en el modo de lanzamiento.

Desactivar el optimizador de MSVC para la biblioteca que contiene las constantes de DB ahora nos permitió reducir el tiempo total de compilación de su Proyecto (varias aplicaciones) en modo de lanzamiento de hasta 8 horas a menos de una hora!

Todavía tenemos que descubrir por qué MSVC tiene dificultades para optimizar estos archivos, pero por ahora este cambio alivia mucha presión, ya que ya no tenemos que depender solo de las versiones nocturnas.

Ese hecho, y otros beneficios, como un acoplamiento menos apretado, una mejor reutilización, etc., también mostraron que pasar tiempo dividiendo las "constantes" no era una mala idea después de todo ;-)

Actualización2

Dado que esta pregunta aún recibe algo de atención:
Esto es lo que he estado haciendo en los últimos años:

Ponga cada constante, variable, etc. exactamente en el ámbito que le resulte relevante: si usa una constante solo en un único método, está bien definirla en ese método. Si una sola clase está interesada en ella, déjela como un detalle de implementación privada de esa clase. Lo mismo se aplica para el espacio de nombres, módulo, proyecto, alcance de la empresa. También uso el mismo patrón para funciones auxiliares y similares. (Esto puede no aplicarse al 100% si desarrolla un marco público).

Hacer esto aumenta la reutilización, la capacidad de prueba y la capacidad de mantenimiento en un grado en el que no solo pasa menos tiempo compilando (al menos en C ++), sino también menos tiempo en la corrección de errores, lo que le deja más tiempo para desarrollar nuevas funciones. Al mismo tiempo, el desarrollo de estas funciones será más rápido, ya que puede reutilizar más código con mayor facilidad. Esto supera cualquier ventaja que pueda tener el archivo de constantes centrales por una magnitud.

Eche un vistazo especialmente al Principio de segregación de interfaz y al Principio de responsabilidad única si desea obtener más información.

Si está de acuerdo, vote la respuesta de Caleb ya que esta actualización es básicamente una versión más general de lo que dijo.

Tim Meyer
fuente
2
personalmente, no tendría el título de UI ni las cadenas de mensajes en constantes. Los tendría en app.config
jk.
1
Me gusta cómo lo estás haciendo ahora. Entiendo tus desventajas, pero podríamos tener que lidiar con eso.
bigtang
1
He visto exactamente el mismo problema en un gran proyecto de Java ... Una gran interfaz de "constantes", cambie cualquier cosa y espere 15 minutos para que se vuelva a compilar eclipse. Estoy con Caleb: agrupa las constantes a las que pertenecen naturalmente, cerca del código que las usa. Mantenerlos separados porque son constantes es un ejercicio OCD inútil.
Michael Borgwardt
El acoplamiento apretado no es un problema, porque eso es lo que realmente quieres. Usted quiere un cambio en su archivo de constantes afecta posiblemente un montón de archivos de origen. (Por supuesto, también tienes otros problemas).
gnasher729
@ gnasher729 eso solo es cierto si muchas clases usan la misma constante. Nunca querrá que las clases estén estrechamente acopladas a constantes con las que no están relacionadas. Puede que no parezca un problema al principio hasta que intente reutilizarlo en otro proyecto sin copiarlo o ejecutar pruebas aisladas
Tim Meyer

Respuestas:

29

Las constantes que son específicas de una clase deben ir en la interfaz de esa clase.

Las constantes que realmente son opciones de configuración deberían formar parte de una clase de configuración. Si proporciona accesores para las opciones de configuración en esa clase (y los usa en lugar de las constantes en otro lugar), no tendrá que volver a compilar todo el mundo cuando cambie algunas opciones.

Las constantes que se comparten entre clases pero que no están destinadas a ser configurables deben tener un alcance razonable: intente dividirlas en archivos que tengan usos particulares para que las clases individuales solo incluyan lo que realmente necesitan. De nuevo, esto ayudará a reducir los tiempos de compilación cuando cambie algunas de esas constantes.

Caleb
fuente
1
En caso de que esté interesado: actualicé mi pregunta con lo que he logrado, siguiendo su respuesta y otras
Tim Meyer,
6

Simplemente diría que desea dividir su clase de constantes enormes en muchos archivos más pequeños, uno por formulario, por ejemplo. Esto garantiza que no tenga una dependencia tan grande en el archivo de constantes, por lo que agregar o actualizar una cadena no requeriría una recompilación total. Todavía puede almacenar estos archivos en una ubicación central, pero (por ejemplo) tiene 1 archivo con constantes para cada cuadro de diálogo, con un nombre adecuado. Entonces solo puede incluir esos archivos en los archivos de diálogo relevantes, lo que reduce la recompilación masivamente.

También te sugiero que uses algo como las utilidades GetText de GNU para manejar las cadenas, está diseñado para la traducción, pero funciona igual de bien para simplemente cambiar el texto a otra cosa. Puede ponerlos en un recurso de cadenas, pero creo que es más difícil trabajar con ellos ya que están codificados por ID, las utilidades GetText están codificadas por una cadena original, lo que hace que las cosas sean muy fáciles de desarrollar.

gbjbaanb
fuente
Podría darle una oportunidad. Como la clase de constantes con títulos, etc. se divide en varias estructuras, tal vez podría hacer una clase por estructura como principio. No estamos seguros de lo de GNU, por lo general no queremos cambiar nuestras cadenas en tiempo de ejecución, solo durante el tiempo de desarrollo. Sin embargo, estamos utilizando el mecanismo de traducción de Qt, en caso de que tengamos que traducir a otro idioma en el futuro.
Tim Meyer
En caso de que esté interesado: actualicé mi pregunta con lo que he logrado, siguiendo su respuesta y otras
Tim Meyer,
2

Nota: No soy un desarrollador de C ++ ... pero este es mi pensamiento: debe considerar seguir el comentario de @ jk sobre la diferencia entre usar archivos de configuración. En DotNet, hay un archivo de recursos que se utiliza para almacenar dicha información. En Windows Forms, se mantiene un archivo de recursos de VS para cada formulario.

No veo un valor para que una constante se coloque fuera de su alcance de uso a menos que sea una constante global que deba compartirse. Como mencionó, esto será difícil de mantener durante el desarrollo al menos. Además, puede obtener conflictos de nombres. Otra cosa es que puede ser difícil saber quién está usando una constante dada.

Ahora, si desea que el no programador revise la información, entonces, para la GUI, captura la pantalla para ellos. Si desea que revisen las entradas de la tabla de datos, puede exportar los datos a Excel o algo similar.

Si aún desea utilizar un enfoque de ubicación centralizada y desea colocar todas sus constantes en un archivo grande, cada desarrollador puede usar un archivo compartido que se actualiza al final de cada intervalo en un archivo central. Los datos provendrán de archivos individuales utilizados en el desarrollo. Esto puede automatizarse fácilmente o hacerse manualmente. Sin embargo, como dije, probablemente es un riesgo que no tiene que correr.

Ninguna posibilidad
fuente
2

No hay una solución general. Pregúntese sobre el rendimiento, la usabilidad, la seguridad y el ciclo de vida de una constante.

Cuanto más cerca se definan de su alcance, mayor será el rendimiento.

Cuanto más se agrupen lógicamente y fuera de su alcance, mayor será la reutilización.

Cuanto menos accesibles son los costantes, mayor es la seguridad.

Cuanto mayor es la vida útil de una constante, menos le importa dónde la pones para usarla.

Una constante como un número de versión se definirá en algún tipo de manifiesto. Se definirá un código de error de una función de error dentro de la clase. Es probable que el código de error sea algo con una vida útil alta (= casi nunca cambia). Ponerlo en un archivo constante solo genera spam en el archivo con cosas innecesarias.

Cuanto menos tenga la constante el carácter de una constante pero una variable (como el número de versión), más podrá ponerla fuera. Cuanto menos variable es la constante, por lo tanto, cuanto más constante es, más debe colocarse dentro de su alcance. Durante la depuración, tiene sentido colocarlo afuera para reducir el tiempo de compilación.

Sin embargo, su problema inicial es el tiempo de compilación. Entonces la pregunta es si haces la pregunta correcta. Si el tiempo de compilación de su aplicación es demasiado alto, debería pensar en una forma de hacerlo más modular para que las piezas funcionen de forma independiente entre sí. Compílalo parcialmente y prueba tus cosas de forma independiente. Si las pruebas unitarias se realizan correctamente y son completas (lo que en realidad es mucho trabajo), puede cambiar las cosas con bastante facilidad sin tener que preocuparse por eso. Y luego la pregunta tiene un impulso totalmente diferente.

cristiano
fuente
1

Sugeriría poner todas estas constantes en algún tipo de archivo de configuración. Para aplicaciones Java, usualmente usamos archivos .properties, un texto simple con cada línea formateada como "(clave) = (valor)". Ejemplo

MainPanel.Title = Bienvenido a nuestra aplicación
DB.table.users = TBL_USERS
logging.filename = application.log

Luego carga este archivo en tiempo de ejecución, llena un caché que le permite buscar una clave y recuperar un valor. Cuando necesita una constante, consulta el caché. Aún necesitará tener sus claves en algún lugar, y el caché deberá ser accesible globalmente, pero cuando cambie los valores reales de las constantes, no es necesario volver a compilar, debería ser posible reiniciar la aplicación (o si realmente quieres ponerte elegante, tener múltiples archivos .properties y cachés y darle a la aplicación la capacidad de recargar un caché en tiempo de ejecución).

Para una implementación, encontré esta pregunta SO: https://stackoverflow.com/questions/874052/properties-file-library-for-c-or-c (fue el primer éxito en una búsqueda en Google, no lo he hecho en realidad usé este software yo mismo).

FrustratedWithFormsDesigner
fuente
Para los proyectos de C ++, solo necesitamos recompilar el archivo de constantes si cambiamos un valor. Esto se debe a que los valores se asignan en un archivo .cpp. Sin embargo, agregar / eliminar / renombrar / mover una constante aún requiere una reconstrucción completa
Tim Meyer
@TimMeyer: si divide las constantes en varios archivos, agregar / eliminar una constante solo afectaría a los archivos que dependen de ese archivo en particular, ¿correcto?
FrustratedWithFormsDesigner
Correcto. El problema principal es que si sugiero eso, las personas tienden a mencionar una de las cosas que enumeré como "ventajas" se pierden
Tim Meyer,
Sin embargo, no he pensado en poner varios archivos constantes en el mismo lugar
Tim Meyer
+1. @Tim Meyer: para este propósito, la verificación en tiempo de compilación cuesta mucho más de lo que ahorra. Además, ¿qué vas a hacer si necesitas internacionalizar?
Kevin Cline