Creo que esta pregunta debería aplicarse a la mayoría de los programas que cargan configuraciones desde un archivo. Mi pregunta es desde el punto de vista de la programación, y realmente es cómo lidiar con la carga de configuraciones desde un archivo en términos de diferentes clases y accesibilidad. Por ejemplo:
- Si un programa tuviera un
settings.ini
archivo simple , ¿su contenido debería cargarse en unload()
método de una clase, o quizás en el constructor? - ¿Deberían almacenarse los valores en
public static
variables, o debería haberstatic
métodos para obtener y establecer propiedades? - ¿Qué debería suceder en caso de que el archivo no exista o no sea legible? ¿Cómo le harías saber al resto del programa que no puede obtener esas propiedades?
- etc.
Espero preguntar esto en el lugar correcto aquí. Quería hacer la pregunta lo más agnóstica del lenguaje posible, pero me estoy centrando principalmente en lenguajes que tienen cosas como la herencia, especialmente Java y C # .NET.
Respuestas:
Esta es realmente una pregunta realmente importante y a menudo se hace mal, ya que no se le da suficiente importancia a pesar de que es una parte central de casi todas las aplicaciones. Aquí están mis pautas:
Su clase de configuración, que contiene todas las configuraciones, debería ser simplemente un tipo de datos antiguo, struct / class:
No debería necesitar métodos y no debería implicar herencia (a menos que sea la única opción que tiene en su idioma para implementar un campo variante; consulte el siguiente párrafo). Puede y debe usar composición para agrupar las configuraciones en clases de configuración específicas más pequeñas (por ejemplo, subConfig arriba). Si lo hace de esta manera, será ideal pasar las pruebas unitarias y la aplicación en general, ya que tendrá dependencias mínimas.
Es probable que necesite usar tipos de variantes, en caso de que las configuraciones para diferentes configuraciones tengan una estructura heterogénea. Se acepta que necesitará colocar una conversión dinámica en algún momento cuando lea el valor para convertirlo a la clase de (sub) configuración correcta, y sin duda esto dependerá de otra configuración.
No debe ser flojo al escribir en todas las configuraciones como campos simplemente haciendo esto:
Esto es tentador, ya que significa que puede escribir una clase de serialización generalizada que no necesita saber con qué campos se trata, pero está mal y explicaré por qué en un momento.
La serialización de la configuración se realiza en una clase completamente separada. Cualquiera sea la API o biblioteca que use para hacer esto, el cuerpo de su función de serialización debe contener entradas que básicamente equivalen a ser un mapa desde la ruta / clave en el archivo hasta el campo en el objeto. Algunos lenguajes proporcionan una buena introspección y pueden hacer esto de forma inmediata, otros tendrá que escribir explícitamente el mapeo, pero la clave es que solo debe escribir el mapeo una vez. Por ejemplo, considere este extracto que adapté de la documentación del analizador de opciones del programa c ++ boost:
Tenga en cuenta que la última línea básicamente dice "optimización" asigna a Config :: opt y también que hay una declaración del tipo que espera. Desea que la lectura de la configuración falle si el tipo no es el esperado, si el parámetro en el archivo no es realmente flotante o int, o no existe. Es decir, debe producirse un error al leer el archivo porque el problema está en el formato / validación del archivo y debe arrojar un código de excepción / retorno e informar el problema exacto. No debe retrasar esto para más adelante en el programa. Es por eso que no debería tener la tentación de atrapar todas las Conf de estilo Diccionario como se mencionó anteriormente, lo que no fallará cuando se lea el archivo, ya que la conversión se retrasa hasta que se necesita el valor.
Debe hacer que la clase Config sea de solo lectura de alguna manera: establezca el contenido de la clase una vez cuando la cree e inicialice desde el archivo. Si necesita tener configuraciones dinámicas en su aplicación que cambien, así como constantes que no lo hacen, debe tener una clase separada para manejar las dinámicas en lugar de tratar de permitir que los bits de su clase de configuración no sean de solo lectura .
Idealmente, lee en el archivo en un lugar de su programa, es decir, solo tiene una instancia de "
ConfigReader
". Sin embargo, si está luchando para que la instancia de Config pase a donde la necesita, es mejor tener un segundo ConfigReader que introducir una configuración global (que supongo que es lo que el OP quiere decir con "static "), Lo que me lleva a mi siguiente punto:Evita la canción de sirena seductora del singleton: "Te ahorraré tener que pasar esa clase de clase, todos tus constructores serán encantadores y limpios. Continúa, será muy fácil". La verdad es que con una arquitectura comprobable bien diseñada, difícilmente necesitará pasar la clase Config o partes de ella a través de tantas clases de su aplicación. Lo que encontrará, en su clase de nivel superior, su función main () o lo que sea, desentrañará la conf en valores individuales, que proporcionará a sus clases de componentes como argumentos que luego volverá a armar (dependencia manual inyección). Una configuración única / global / estática hará que las pruebas unitarias de su aplicación sean mucho más difíciles de implementar y comprender, por ejemplo, confundirá a los nuevos desarrolladores con su equipo que no sabrán que tienen que configurar el estado global para probar cosas.
Si su idioma admite propiedades, debe usarlas para este propósito. La razón es que significa que será muy fácil agregar configuraciones de configuración 'derivadas' que dependen de una o más configuraciones. p.ej
Si su idioma no admite de forma nativa el idioma de propiedad, puede tener una solución alternativa para lograr el mismo efecto, o simplemente cree una clase de contenedor que proporcione la configuración de bonificación. Si de lo contrario no puede conferir el beneficio de las propiedades, de lo contrario es una pérdida de tiempo escribir manualmente y usar captadores / establecedores simplemente con el propósito de complacer a algún dios. Estarás mejor con un campo viejo y llano.
Es posible que necesite un sistema para fusionar y tomar múltiples configuraciones de diferentes lugares en orden de precedencia. Ese orden de precedencia debe estar bien definido y entendido por todos los desarrolladores / usuarios, por ejemplo, considere el registro de Windows HKEY_CURRENT_USER / HKEY_LOCAL_MACHINE. Debe hacer este estilo funcional para que pueda mantener sus configuraciones de solo lectura, es decir:
más bien que:
Finalmente, debería agregar eso, por supuesto, si el marco / lenguaje elegido proporciona sus propios mecanismos de configuración incorporados y conocidos, debería considerar los beneficios de usar eso en lugar de crear el suyo.
Entonces. Hay muchos aspectos a tener en cuenta: acéptelo bien y afectará profundamente la arquitectura de su aplicación, reduciendo errores, haciendo que las cosas sean fácilmente comprobables y obligándolo a usar un buen diseño en otros lugares.
fuente
.ini
para que sea legible para los humanos, pero ¿sugieres que debería serializar una clase con las variables?En general (en mi opinión), es mejor dejar que la aplicación se ocupe de cómo se almacena la configuración y pasarla a sus módulos. Esto permite flexibilidad en cómo se guardan las configuraciones para que pueda apuntar a archivos o servicios web o bases de datos o ...
Además, pone la carga de "lo que sucede cuando las cosas fallan" en la aplicación, quién sabe mejor qué significa esa falla.
Y hace que sea mucho más fácil realizar pruebas unitarias cuando solo puede pasar un objeto de configuración en lugar de tener que tocar el sistema de archivos o tratar los problemas de concurrencia introducidos por el acceso estático.
fuente
Si está utilizando .NET para programar las clases, tiene diferentes opciones como Recursos, web.config o incluso un archivo personalizado.
Si usa Resources o web.config, los datos se almacenan realmente en un archivo XML, pero la carga es más rápida.
Recuperar los datos de estos archivos y almacenarlos en otra ubicación será como un doble uso de la memoria, ya que estos se cargan en la memoria de forma predeterminada.
Para cualquier otro archivo o lenguaje de programación, la respuesta anterior de Benedict funcionará.
fuente