Una manera simple de mantener la configuración de una aplicación Java está representada por un archivo de texto con la extensión ".properties" que contiene el identificador de cada configuración asociada con un valor específico (este valor puede ser un número, cadena, fecha, etc.) . C # utiliza un enfoque similar, pero el archivo de texto debe llamarse "App.config". En ambos casos, en el código fuente debe inicializar una clase específica para leer la configuración: esta clase tiene un método que devuelve el valor (como cadena) asociado con el identificador de configuración especificado.
// Java example
Properties config = new Properties();
config.load(...);
String valueStr = config.getProperty("listening-port");
// ...
// C# example
NameValueCollection setting = ConfigurationManager.AppSettings;
string valueStr = setting["listening-port"];
// ...
En ambos casos, deberíamos analizar las cadenas cargadas desde el archivo de configuración y asignar los valores convertidos a los objetos mecanografiados relacionados (podrían ocurrir errores de análisis durante esta fase). Después del paso de análisis, debemos verificar que los valores de configuración pertenecen a un dominio específico de validez: por ejemplo, el tamaño máximo de una cola debe ser un valor positivo, algunos valores pueden estar relacionados (ejemplo: min <max ), y así.
Suponga que la aplicación debe cargar la configuración tan pronto como se inicia: en otras palabras, la primera operación realizada por la aplicación es cargar la configuración. Cualquier valor no válido para la configuración debe reemplazarse automáticamente con los valores predeterminados: si esto le sucede a un grupo de configuraciones relacionadas, esas configuraciones se establecerán con valores predeterminados.
La forma más fácil de realizar estas operaciones es crear un método que primero analice todas las configuraciones, luego verifique los valores cargados y finalmente establezca los valores predeterminados. Sin embargo, el mantenimiento es difícil si utiliza este enfoque: a medida que aumenta el número de configuraciones mientras se desarrolla la aplicación, se vuelve cada vez más difícil actualizar el código.
Para resolver este problema, pensé en usar el patrón Método de plantilla , como sigue.
public abstract class Setting
{
protected abstract bool TryParseValues();
protected abstract bool CheckValues();
public abstract void SetDefaultValues();
/// <summary>
/// Template Method
/// </summary>
public bool TrySetValuesOrDefault()
{
if (!TryParseValues() || !CheckValues())
{
// parsing error or domain error
SetDefaultValues();
return false;
}
return true;
}
}
public class RangeSetting : Setting
{
private string minStr, maxStr;
private byte min, max;
public RangeSetting(string minStr, maxStr)
{
this.minStr = minStr;
this.maxStr = maxStr;
}
protected override bool TryParseValues()
{
return (byte.TryParse(minStr, out min)
&& byte.TryParse(maxStr, out max));
}
protected override bool CheckValues()
{
return (0 < min && min < max);
}
public override void SetDefaultValues()
{
min = 5;
max = 10;
}
}
El problema es que de esta manera necesitamos crear una nueva clase para cada configuración, incluso para un solo valor. ¿Hay otras soluciones para este tipo de problema?
En resumen:
- Fácil mantenimiento: por ejemplo, la adición de uno o más parámetros.
- Extensibilidad: una primera versión de la aplicación podría leer un solo archivo de configuración, pero las versiones posteriores pueden dar la posibilidad de una configuración multiusuario (el administrador configura una configuración básica, los usuarios pueden establecer solo ciertas configuraciones, etc.).
- Diseño orientado a objetos.
fuente
Respuestas:
Esencialmente, el archivo de configuración externo está codificado como un documento YAML. Esto se analiza durante el inicio de la aplicación y se asigna a un objeto de configuración.
El resultado final es robusto y, sobre todo, simple de gestionar.
fuente
Consideremos esto desde dos puntos de vista: la API para obtener los valores de configuración y el formato de almacenamiento. A menudo están relacionados, pero es útil considerarlos por separado.
API de configuración
El patrón del Método de plantilla es muy general, pero me pregunto si realmente necesita esa generalidad. Necesitaría una clase para cada tipo de valor de configuración. ¿Realmente tienes tantos tipos? Supongo que podrías sobrevivir con solo un puñado: cadenas, ints, flotantes, booleanos y enumeraciones. Dado esto, podría tener una
Config
clase que tenga un puñado de métodos:(Creo que obtuve los genéricos de ese último correcto).
Básicamente, cada método sabe cómo manejar el análisis del valor de cadena desde el archivo de configuración y manejar los errores y devolver el valor predeterminado, si corresponde. La comprobación de rango para los valores numéricos es probablemente suficiente. Es posible que desee tener sobrecargas que omitan los valores de rango, lo que sería equivalente a proporcionar un rango de Integer.MIN_VALUE, Integer.MAX_VALUE. Una enumeración es una forma segura de escribir de una cadena contra un conjunto fijo de cadenas.
Hay algunas cosas que esto no maneja, como valores múltiples, valores que están interrelacionados, búsquedas dinámicas de tablas, etc. Podría escribir rutinas especializadas de análisis y validación para estas, pero si esto se complica demasiado, comenzaría a cuestionar si estás intentando hacer demasiado con un archivo de configuración.
Formato de almacenamiento
Los archivos de propiedades Java parecen estar bien para almacenar pares clave-valor individuales, y admiten bastante bien los tipos de valores que describí anteriormente. También podría considerar otros formatos como XML o JSON, pero estos probablemente sean excesivos a menos que tenga datos anidados o repetidos. En ese punto, parece mucho más allá de un archivo de configuración ...
Telastyn mencionó objetos serializados. Esta es una posibilidad, aunque la serialización tiene sus dificultades. Es binario, no texto, por lo que es difícil ver y editar los valores. Tienes que lidiar con la compatibilidad de serialización. Si faltan valores en la entrada serializada (por ejemplo, agregó un campo a la clase Config y está leyendo una forma serializada anterior), los nuevos campos se inicializan a nulo / cero. Debe escribir lógica para determinar si debe completar algún otro valor predeterminado. ¿Pero un cero indica ausencia de un valor de configuración, o se especificó que era cero? Ahora tienes que depurar esta lógica. Finalmente (no estoy seguro de si esto es una preocupación) aún puede necesitar validar valores en la secuencia de objetos serializados. Es posible (aunque inconveniente) que un usuario malintencionado modifique una secuencia de objetos serializados de forma indetectable.
Yo diría que seguir con las propiedades si es posible.
fuente
Config
clase y utilizar el enfoque propuesto por usted:getInt()
,getByte()
,getBoolean()
, etc .. Continuando con esta idea, leí por primera vez todos los valores y pude asociar cada valor de una bandera (este indicador es falso si se produjo un problema durante la deserialización, por ejemplo, errores de análisis). Después de eso, podría comenzar una fase de validación para todos los valores cargados y establecer los valores predeterminados.Como lo he hecho:
Inicialice todo a los valores predeterminados.
Analiza el archivo y almacena los valores a medida que avanzas. Los lugares que se establecen son responsables de garantizar que los valores sean aceptables, se ignoren los valores incorrectos (y, por lo tanto, conserven el valor predeterminado).
fuente
Si todo lo que necesita es una configuración simple, me gusta hacer una clase simple para ella. Inicializa los valores predeterminados y la aplicación puede cargarlos desde el archivo a través de las clases de serialización integradas. La aplicación luego lo pasa a cosas que lo necesitan. Sin reflexionar con el análisis o las conversiones, sin perder el tiempo con las cadenas de configuración, sin arrojar basura. Y hace que la configuración del modo más fácil de usar para los escenarios de código en las que debe guardar / cargar desde un servidor o como preajustes, y forma más fácil de usar en las pruebas unitarias.
fuente
Al menos en .NET, puede crear fácilmente sus propios objetos de configuración fuertemente tipados; consulte este artículo de MSDN para ver un ejemplo rápido.
Protip: envuelva su clase de configuración en una interfaz y deje que su aplicación hable con eso. Facilita la inyección de configuraciones falsas para pruebas o con fines de lucro.
fuente
ConfigurationElement
clase podría representar un grupo de valores, y para cualquier valor puede especificar un validador. Pero si, por ejemplo, quisiera representar un elemento de configuración que consta de cuatro probabilidades, los cuatro valores de probabilidad están correlacionados, ya que su suma debe ser igual a 1. ¿Cómo valido este elemento de configuración?