Un moyen simple de conserver les paramètres d'une application Java est représenté par un fichier texte avec l'extension ".properties" contenant l'identifiant de chaque paramètre associé à une valeur spécifique (cette valeur peut être un nombre, une chaîne, une date, etc.) . C # utilise une approche similaire, mais le fichier texte doit être nommé "App.config". Dans les deux cas, dans le code source, vous devez initialiser une classe spécifique pour la lecture des paramètres: cette classe a une méthode qui renvoie la valeur (sous forme de chaîne) associée à l'identificateur de paramètre spécifié.
// 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"];
// ...
Dans les deux cas, nous devons analyser les chaînes chargées à partir du fichier de configuration et affecter les valeurs converties aux objets typés associés (des erreurs d'analyse peuvent se produire pendant cette phase). Après l'étape d'analyse, nous devons vérifier que les valeurs des paramètres appartiennent à un domaine de validité spécifique: par exemple, la taille maximale d'une file d'attente doit être une valeur positive, certaines valeurs peuvent être liées (exemple: min <max ), etc.
Supposons que l'application charge les paramètres dès son démarrage: en d'autres termes, la première opération effectuée par l'application consiste à charger les paramètres. Toutes les valeurs non valides pour les paramètres doivent être remplacées automatiquement par des valeurs par défaut: si cela se produit pour un groupe de paramètres associés, ces paramètres sont tous définis avec des valeurs par défaut.
La façon la plus simple d'effectuer ces opérations consiste à créer une méthode qui analyse d'abord tous les paramètres, puis vérifie les valeurs chargées et définit enfin les valeurs par défaut. Cependant, la maintenance est difficile si vous utilisez cette approche: à mesure que le nombre de paramètres augmente lors du développement de l'application, il devient de plus en plus difficile de mettre à jour le code.
Afin de résoudre ce problème, j'avais pensé à utiliser le modèle de méthode de modèle, comme suit.
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;
}
}
Le problème est que de cette façon, nous devons créer une nouvelle classe pour chaque paramètre, même pour une seule valeur. Existe-t-il d'autres solutions à ce type de problème?
En résumé:
- Maintenance facile: par exemple, l'ajout d'un ou plusieurs paramètres.
- Extensibilité: une première version de l'application pourrait lire un seul fichier de configuration, mais les versions ultérieures peuvent donner la possibilité d'une configuration multi-utilisateurs (l'administrateur configure une configuration de base, les utilisateurs ne peuvent définir que certains paramètres, etc.).
- Conception orientée objet.
la source
Réponses:
Le fichier de configuration externe est essentiellement codé en tant que document YAML. Ceci est ensuite analysé lors du démarrage de l'application et mappé à un objet de configuration.
Le résultat final est robuste et surtout simple à gérer.
la source
Examinons cela de deux points de vue: l'API pour obtenir les valeurs de configuration et le format de stockage. Ils sont souvent liés, mais il est utile de les considérer séparément.
API de configuration
Le modèle de méthode de modèle est très général, mais je me demande si vous avez vraiment besoin de cette généralité. Vous auriez besoin d'une classe pour chaque type de valeur de configuration. Avez-vous vraiment autant de types? Je suppose que vous pourriez vous en tirer avec seulement une poignée: chaînes, entiers, flotteurs, booléens et énumérations. Compte tenu de ceux-ci, vous pourriez avoir une
Config
classe qui a une poignée de méthodes:(Je pense que j'ai bien compris les génériques sur ce dernier.)
Fondamentalement, chaque méthode sait comment gérer l'analyse de la valeur de chaîne du fichier de configuration et gérer les erreurs et renvoyer la valeur par défaut, le cas échéant. La vérification des plages pour les valeurs numériques est probablement suffisante. Vous souhaiterez peut-être des surcharges qui omettent les valeurs de plage, ce qui équivaudrait à fournir une plage de Integer.MIN_VALUE, Integer.MAX_VALUE. Une énumération est un moyen sûr de type de valider une chaîne par rapport à un ensemble fixe de chaînes.
Il y a certaines choses que cela ne gère pas, telles que les valeurs multiples, les valeurs qui sont interdépendantes, les recherches de tables dynamiques, etc. si vous essayez d'en faire trop avec un fichier de configuration.
Format de stockage
Les fichiers de propriétés Java semblent bien pour stocker des paires clé-valeur individuelles, et ils prennent assez bien en charge les types de valeurs que j'ai décrits ci-dessus. Vous pouvez également envisager d'autres formats tels que XML ou JSON, mais ceux-ci sont probablement exagérés, sauf si vous avez des données imbriquées ou répétées. À ce stade, il semble bien au-delà d'un fichier de configuration ....
Telastyn a mentionné les objets sérialisés. C'est une possibilité, bien que la sérialisation ait ses difficultés. C'est binaire, pas de texte, il est donc difficile de voir et de modifier les valeurs. Vous devez gérer la compatibilité de sérialisation. Si des valeurs manquent dans l'entrée sérialisée (par exemple, vous avez ajouté un champ à la classe Config et que vous en lisez une ancienne forme sérialisée), les nouveaux champs sont initialisés à null / zéro. Vous devez écrire une logique pour déterminer s'il faut remplir une autre valeur par défaut. Mais un zéro indique-t-il l'absence d'une valeur de configuration, ou a-t-il été spécifié comme étant zéro? Vous devez maintenant déboguer cette logique. Enfin (je ne sais pas s'il s'agit d'un problème), vous devrez peut-être encore valider les valeurs dans le flux d'objets sérialisés. Il est possible (bien que peu pratique) pour un utilisateur malveillant de modifier un flux d'objets sérialisés de manière indétectable.
Je dirais de m'en tenir aux propriétés si possible.
la source
Config
classe et utiliser l'approche proposée par vous:getInt()
,getByte()
,getBoolean()
, etc .. continue avec cette idée, je lis d' abord toutes les valeurs et je pouvais associer chaque valeur à un drapeau (cet indicateur est faux si un problème s'est produit pendant la désérialisation, par exemple des erreurs d'analyse). Après cela, j'ai pu démarrer une phase de validation pour toutes les valeurs chargées et définir toutes les valeurs par défaut.Comment je l'ai fait:
Initialisez tout aux valeurs par défaut.
Analysez le fichier en stockant les valeurs au fur et à mesure. Les emplacements définis sont chargés de garantir que les valeurs sont acceptables, les mauvaises valeurs sont ignorées (et conservent ainsi la valeur par défaut.)
la source
Si tout ce dont vous avez besoin est une configuration simple, j'aime en faire une ancienne classe. Il initialise les valeurs par défaut et peut être chargé à partir d'un fichier par l'application via les classes de sérialisation intégrées. L'application la transmet ensuite aux éléments qui en ont besoin. Pas de bavardage avec l'analyse ou les conversions, pas de vissage avec des chaînes de configuration, pas de lancer des ordures. Et il rend la configuration ainsi plus facile à utiliser pour des scénarios en code où il doit être enregistré / chargé à partir du serveur ou sous forme de presets, et ainsi plus facile à utiliser dans vos tests unitaires.
la source
Au moins dans .NET, vous pouvez assez facilement créer vos propres objets de configuration fortement typés - consultez cet article MSDN pour un exemple rapide.
Protip: enveloppez votre classe de configuration dans une interface et laissez votre application en parler. Il est facile d'injecter une fausse configuration pour des tests ou à but lucratif.
la source
ConfigurationElement
classe de classe pourrait représenter un groupe de valeurs, et pour toute valeur, vous pouvez spécifier un validateur. Mais si, par exemple, je voulais représenter un élément de configuration composé de quatre probabilités, les quatre valeurs de probabilité sont corrélées, car leur somme doit être égale à 1. Comment puis-je valider cet élément de configuration?