Je pense que cette question devrait s'appliquer à la plupart des programmes qui chargent les paramètres d'un fichier. Ma question est du point de vue de la programmation, et c'est vraiment comment gérer le chargement des paramètres d'un fichier en termes de classes différentes et d'accessibilité. Par exemple:
- Si un programme avait un
settings.ini
fichier simple , son contenu devrait-il être chargé dans uneload()
méthode d'une classe, ou peut-être le constructeur? - Les valeurs doivent-elles être stockées dans des
public static
variables, ou doit-il y avoir desstatic
méthodes pour obtenir et définir des propriétés? - Que doit-il se passer si le fichier n’existe pas ou n’est pas lisible? Comment pourriez-vous faire savoir au reste du programme qu'il ne peut pas obtenir ces propriétés?
- etc.
J'espère que je pose cette question au bon endroit ici. Je voulais rendre la question aussi indépendante du langage que possible, mais je me concentre principalement sur les langages qui ont des choses comme l'héritage - en particulier Java et C # .NET.
Réponses:
C'est en fait une question vraiment importante et elle est souvent mal faite car on ne lui donne pas suffisamment d'importance même si elle est au cœur de presque toutes les applications. Voici mes directives:
Votre classe de configuration, qui contient tous les paramètres, doit être simplement un ancien type de données simple, struct / class:
Il ne devrait pas avoir besoin de méthodes et ne devrait pas impliquer d'héritage (sauf si c'est le seul choix que vous avez dans votre langue pour implémenter un champ variant - voir le paragraphe suivant). Il peut et doit utiliser la composition pour regrouper les paramètres dans des classes de configuration spécifiques plus petites (par exemple, subConfig ci-dessus). Si vous le faites de cette façon, il sera idéal de passer dans les tests unitaires et l'application en général car elle aura des dépendances minimales.
Vous devrez probablement utiliser des types de variantes, dans le cas où les configurations pour différentes configurations sont hétérogènes dans la structure. Il est admis que vous aurez besoin de placer un cast dynamique à un moment donné lorsque vous lisez la valeur pour le cast dans la bonne (sous-) classe de configuration, et cela dépendra sans aucun doute d'un autre paramètre de configuration.
Vous ne devriez pas être paresseux à taper tous les paramètres en tant que champs en faisant simplement ceci:
C'est tentant car cela signifie que vous pouvez écrire une classe de sérialisation généralisée qui n'a pas besoin de savoir avec quels champs il s'agit, mais c'est faux et j'expliquerai pourquoi dans un instant.
La sérialisation de la configuration se fait dans une classe complètement distincte. Quelle que soit l'API ou la bibliothèque que vous utilisez pour ce faire, le corps de votre fonction de sérialisation doit contenir des entrées qui reviennent essentiellement à être une carte du chemin / clé du fichier vers le champ de l'objet. Certaines langues offrent une bonne introspection et peuvent le faire à votre place, d'autres vous devrez écrire explicitement le mappage, mais l'essentiel est que vous ne deviez avoir à écrire le mappage qu'une seule fois. Par exemple, considérez cet extrait que j'ai adapté de la documentation de l'analyseur des options du programme c ++ boost:
Notez que la dernière ligne dit essentiellement "optimisation" mappe sur Config :: opt et également qu'il existe une déclaration du type que vous attendez. Vous voulez que la lecture de la configuration échoue si le type n'est pas celui que vous attendez, si le paramètre dans le fichier n'est pas vraiment un flottant ou un int, ou n'existe pas. C'est-à-dire qu'un échec doit se produire lorsque vous lisez le fichier car le problème est lié au format / validation du fichier et vous devez lancer un code d'exception / retour et signaler le problème exact. Vous ne devez pas retarder cela plus tard dans le programme. C'est pourquoi vous ne devriez pas être tenté d'avoir une capture de tout le style de dictionnaire Conf comme mentionné ci-dessus qui n'échouera pas lors de la lecture du fichier - car la conversion est retardée jusqu'à ce que la valeur soit nécessaire.
Vous devez rendre la classe Config en lecture seule d'une certaine manière - en définissant le contenu de la classe une fois lorsque vous la créez et l'initialisez à partir du fichier. Si vous devez avoir des paramètres dynamiques dans votre application qui changent, ainsi que des paramètres const qui ne le font pas, vous devriez avoir une classe distincte pour gérer les dynamiques plutôt que d'essayer de permettre aux bits de votre classe de configuration de ne pas être en lecture seule .
Idéalement, vous lisez le fichier à un endroit de votre programme, c'est-à-dire que vous n'avez qu'une seule instance de "
ConfigReader
". Cependant, si vous avez du mal à faire passer l'instance Config là où vous en avez besoin, il est préférable d'avoir un deuxième ConfigReader que d'introduire une configuration globale (ce que je suppose, c'est ce que l'OP signifie par "statique"). "), Ce qui m'amène à mon prochain point:Évitez la chanson sirène séduisante du singleton: "Je vous éviterai d'avoir à passer cette classe, tous vos constructeurs seront charmants et propres. Allez, ce sera si facile." La vérité est qu'avec une architecture testable bien conçue, vous n'aurez guère besoin de passer la classe Config, ou des parties de celle-ci à travers autant de classes de votre application. Ce que vous trouverez, dans votre classe de niveau supérieur, votre fonction main () ou quoi que ce soit, vous démêlerez la conf en valeurs individuelles, que vous fournirez à vos classes de composants sous forme d'arguments que vous remettrez ensuite ensemble (dépendance manuelle injection). Une conf singleton / global / statique rendra les tests unitaires de votre application beaucoup plus difficiles à implémenter et à comprendre - par exemple, cela confondra les nouveaux développeurs avec votre équipe qui ne saura pas qu'ils doivent définir l'état global pour tester les choses.
Si votre langue prend en charge les propriétés, vous devez les utiliser à cette fin. La raison en est que cela signifie qu'il sera très facile d'ajouter des paramètres de configuration «dérivés» qui dépendent d'un ou plusieurs autres paramètres. par exemple
Si votre langue ne prend pas en charge nativement l'idiome de la propriété, il peut y avoir une solution de contournement pour obtenir le même effet, ou vous créez simplement une classe wrapper qui fournit les paramètres bonus. Si vous ne pouvez pas autrement conférer l'avantage des propriétés, c'est sinon une perte de temps pour écrire manuellement et utiliser des getters / setters simplement dans le but de plaire à un dieu OO. Vous serez mieux avec un vieux champ ordinaire.
Vous pourriez avoir besoin d'un système pour fusionner et prendre plusieurs configurations de différents endroits par ordre de priorité. Cet ordre de priorité doit être bien défini et compris par tous les développeurs / utilisateurs, par exemple, considérer le registre Windows HKEY_CURRENT_USER / HKEY_LOCAL_MACHINE. Vous devriez faire ce style fonctionnel pour pouvoir garder vos configurations en lecture seule, c'est-à-dire:
plutôt que:
Je devrais enfin ajouter que, bien sûr, si votre framework / langage choisi fournit ses propres mécanismes de configuration intégrés et bien connus, vous devriez considérer les avantages de l'utiliser au lieu de rouler les vôtres.
Donc. Beaucoup d'aspects à prendre en compte - faites-le bien et cela affectera profondément l'architecture de votre application, réduisant les bogues, rendant les choses facilement testables et vous obligeant en quelque sorte à utiliser une bonne conception ailleurs.
la source
.ini
afin qu'il soit lisible par l'homme, mais proposez-vous que je devrais sérialiser une classe avec les variables en?En général (à mon avis), il est préférable de laisser l'application gérer la façon dont la configuration est stockée et de transmettre la configuration à vos modules. Cela permet une flexibilité dans la façon dont les paramètres sont enregistrés afin que vous puissiez cibler des fichiers ou des services Web ou des bases de données ou ...
De plus, cela met le fardeau de "ce qui se passe quand les choses échouent" sur l'application, qui sait le mieux ce que signifie cet échec.
Et cela rend les tests unitaires beaucoup plus faciles lorsque vous pouvez simplement passer un objet de configuration plutôt que d'avoir à toucher le système de fichiers ou à traiter les problèmes de concurrence introduits par l'accès statique.
la source
Si vous utilisez .NET pour programmer les classes, vous avez différentes options telles que Resources, web.config ou même un fichier personnalisé.
Si vous utilisez Resources ou web.config, les données sont réellement stockées dans un fichier XML, mais le chargement est plus rapide.
Récupérer les données de ces fichiers et les stocker dans un autre emplacement sera comme une double utilisation de la mémoire car celles-ci sont chargées en mémoire par défaut.
Pour tout autre fichier ou langage de programmation, la réponse ci-dessus de Benedict fonctionnera.
la source