Qu'est-ce qu'un objet de configuration et pourquoi s'en soucier?

C'est pénible de devoir modifier les paramètres d'une fonction. vous devez changer tous les autres appels à cette fonction pour éviter les erreurs. Mais vous pouvez contourner cela en utilisant un seul paramètre: un objet de configuration.


À quoi il ressemble

Voici un exemple stupide de fonction permettant de créer un robot:

 function generateRobot (arms: int, personnalité: String): Robot robot var: Robot = nouveau Robot (); pour (var i: int = 0; i < arms; i++)  //create arm and add it to robot  if (personality == "evil")  robot.commands = "Destroy mankind.";  else  robot.commands = "Bake cookies."  return robot;  generateRobot(2, "evil");

Maintenant, voici le même exemple, en utilisant un objet de configuration:

 function generateRobot (conf: Object): Robot robot var: Robot = new Robot (); pour (var i: int = 0; i < conf.arms; i++)  //create arm and add it to robot  if (conf.personality == "evil")  robot.commands = "Destroy mankind.";  else  robot.commands = "Bake cookies."  return robot;  generateRobot(arms:2, personality:"evil");

J'ai mis en évidence les lignes qui doivent être modifiées. vous pouvez voir qu'il n'y a pas beaucoup de différence.


Pourquoi s'embêter?

Donc, s'il n'y a guère de différence, pourquoi devrions-nous nous donner la peine de le faire de la deuxième façon? Après tout, cela rend la fonction un peu plus difficile à utiliser; alors qu'avant notre IDE serait capable de nous donner cette information sur les paramètres de la fonction attendue:

… Maintenant cela ne peut que nous donner ceci:

Supposons que vous souhaitiez ajouter quelques paramètres supplémentaires: l'un spécifiant le matériau à utiliser et l'autre, la couleur de son laser. Ce n'est pas trop difficile, dans les deux cas:

 function generateRobot (arms: int, personnalité: String, matériau: String, laserColor: String): Robot robot var: Robot = nouveau Robot (); pour (var i: int = 0; i < arms; i++)  //create arm and add it to robot  if (personality == "evil")  robot.commands = "Destroy mankind.";  else  robot.commands = "Bake cookies."  switch (material)  case "wood": //wooden robot break; case "steel": default: //steel robot break;  robot.laser = new Laser(); robot.laser.color = laserColor; return robot;  generateRobot(2, "evil", "steel", "red");
 function generateRobot (conf: Object): Robot robot var: Robot = new Robot (); pour (var i: int = 0; i < conf.arms; i++)  //create arm and add it to robot  if (conf.personality == "evil")  robot.commands = "Destroy mankind.";  else  robot.commands = "Bake cookies."  switch (conf.material)  case "wood": //wooden robot break; case "steel": default: //steel robot break;  robot.laser = new Laser(); robot.laser.color = conf.laserColor; return robot;  generateRobot(arms:2, personality:"evil", material:"steel", laserColor:"red");

Jusqu'ici, toujours pas beaucoup de différence. Et si vous voulez que vos robots aient tous des lasers rouges par défaut? Simple encore. Sans objet de configuration, il vous suffit de modifier la signature de la méthode (la une fonction ligne), et vous pouvez ensuite supprimer le dernier argument de l’appel de la fonction:

 function generateRobot (arms: int, personnalité: String, matériau: String, laserColor: String = "rouge"): Robot // c'est la même chose generateRobot (2, vrai, "acier"); // j'ai enlevé le dernier argument

Avec un objet de configuration, c'est un peu plus compliqué - mais pas beaucoup:

 function generateRobot (conf: Object): Robot if (! conf.laserColor) conf.laserColor = "rouge";  var robot: Robot = new Robot (); pour (var i: int = 0; i < conf.arms; i++)  //create arm and add it to robot  if (conf.personality == "evil")  robot.commands = "Destroy mankind.";  else  robot.commands = "Bake cookies."  switch (conf.material)  case "wood": //wooden robot break; case "steel": default: //steel robot break;  robot.laser = new Laser(); robot.laser.color = conf.laserColor; return robot;  generateRobot(arms:2, personality:"evil", material:"steel"); //I removed the last argument

D'accord. Supposons maintenant que vous définissez la quasi-totalité de vos robots comme étant diaboliques (je veux dire, pourquoi pas?), Il est donc plutôt pénible d’écrire "mal" en tant que paramètre à chaque fois. Naturellement, vous voulez définir "mal" comme valeur par défaut - mais vous ne pas vouloir définir un matériau par défaut.

Pour cela, avec un jeu de paramètres de fonction standard, vous pouvez changer l’ordre des commandes. personnalité et Matériel paramètres:

 function generateRobot (arms: int, matériau: String, personnalité: String = "evil", laserColor: String = "rouge"): Robot 

Ah, mais maintenant vous devez changer l'ordre des arguments pour chaque appel de fonction!

 générerRobot (2, "mal", "acier"); //ne fonctionne plus

Un objet de configuration ne vous donne pas ce problème. Vérifiez-le:

 function generateRobot (conf: Object): Robot if (! conf.laserColor) conf.laserColor = "rouge";  if (! conf.personality) conf.personality = "evil" // c'est la même chose generateRobot (arms: 2, material: "steel"); // pas de paramètre "personnalité"? aucun problème!

Soigné! Tous vos vieux generateRobot () les appels de fonction continueront à fonctionner, mais vous pouvez créer de nouveaux appels qui ne vous dérange pas de spécifier personnalité.

Vous pouvez même décider de vous débarrasser de la personnalité paramètre en tout:

 function generateRobot (conf: Object): Robot if (! conf.laserColor) conf.laserColor = "rouge";  if (! conf.personality) conf.personality = "evil" var robot: Robot = new Robot (); pour (var i: int = 0; i < conf.arms; i++)  //create arm and add it to robot  robot.commands = "Destroy mankind."; switch (conf.material)  case "wood": //wooden robot break; case "steel": default: //steel robot break;  robot.laser = new Laser(); robot.laser.color = conf.laserColor; return robot; 

La version ci-dessus de la fonction ne fait pas référence à conf.personality du tout - mais vous n'obtiendrez pas d'erreur si vous avez encore des appels comme celui-ci:

 generateRobot (bras: 2, personnalité: "mal", matériau: "acier");

Bien sûr, vous pourriez avoir quelques utilisateurs confus si vous avez des appels comme celui-ci:

 generateRobot (bras: 2, personnalité: "bien", matériau: "acier");

… Puisque tous les robots sont maintenant pervers. Mais au moins le code compilera.

Pour la même raison, vous pouvez changer l'ordre des arguments sans que cela compte vraiment, et même ajouter de nouveaux paramètres qui ne font rien encore:

 generateRobot (matériau: "acier", laserCouleur: "vert", bras: 2, voix: "M. T");

Faciliter la définition des valeurs par défaut

Le code pour définir les valeurs par défaut est facile à comprendre jusqu'à présent, mais il sera très ennuyeux d'étendre si nous avons besoin de beaucoup de paramètres:

 if (! conf.laserColor) conf.laserColor = "rouge";  if (! conf.personality) conf.personality = "evil"

Écrivons un code plus général pour y faire face:

 var default: Object = laserColor: red, personnalité: "evil" pour (var key: String in defaults) if (! conf [clé]) conf [clé] = par défaut [clé]; 

Cette pour boucle peut être un peu déroutant, alors je vais le décomposer. D'abord, regardez ceci:

 for (var key: chaîne de caractères par défaut) trace (key); 

C'est un pour… dans boucle, qui va sortir les noms des clés à l'intérieur du défaut objet:

 personnalité laserColor

Ensuite, regardez cette ligne:

 trace (par défaut ["laserColor"]);

Cela produira rouge - c'est comme écrire trace (defaults.laserColor).

Suite à cela, regardez cet exemple:

 var exemple: Object = demo: "test"; trace (exemple ["demo"]); trace (exemple ["foo"]);

Que pensez-vous que cela produira?

bien, exemple ["démo"] est le même que exemple.demo, qui est égal à "tester". Mais exemple.foo n'existe pas, donc exemple ["foo"] reviendra nul. Cela signifie que !exemple ["foo"] (notez le point d'exclamation) sera équivalent à vrai.

Mettez tout cela ensemble et vous devriez être capable de comprendre pourquoi ce code fonctionne:

 var default: Object = laserColor: red, personnalité: "evil" pour (var key: String in defaults) if (! conf [clé]) conf [clé] = par défaut [clé]; 

Donnez-moi un cri dans les commentaires si vous avez besoin d'un coup de main!

Je veux plus!

Pour une version encore plus rapide, essayez ceci:

 function generateRobot (conf: Object = null): Robot var conf: Object = conf || ; var default: Object = laserColor: rouge, personnalité: "evil" pour (var key: String in defaults) conf [clé] = conf [clé] || valeurs par défaut [clé]; 

Le changement de la ligne 1 (et de la nouvelle ligne 2) signifie que même le conf l'objet lui-même est facultatif, vous pouvez donc simplement appeler generateRobot (). (Bien sûr, vous devrez changer le code pour traiter les valeurs qui n'ont pas de valeurs par défaut à l'heure actuelle.)


Aider l'EDI à vous aider

Comme je l'ai mentionné ci-dessus, l'EDI ne peut vous conseiller sur les paramètres qu'une fonction attend si elle utilise un objet de configuration. C'est un inconvénient majeur, car cela peut rendre votre code très difficile à utiliser; vous devez vous rappeler quels paramètres vont dans le conf objet, ainsi que tous leurs noms et types.

Mais nous pouvons toujours afficher cette information au codeur quand c'est nécessaire; il suffit de le faire manuellement, comme ceci:

 / ** * Génère un robot en fonction des paramètres donnés. * @param conf Objet de configuration. Attend: * bras (int) Nombre de bras que le robot devrait avoir. * Personality (String) Personnalité du robot. Peut être "mauvais" ou "bon". La valeur par défaut est "le mal". * material (String) En quoi le robot devrait-il être fabriqué? Peut être "acier" ou "bois" à ce moment. * laserColor (String) Couleur du laser du robot. La valeur par défaut est "rouge". * voice (String) Styles vocaux du robot. Actuellement non implémenté. * @retour Le robot terminé. * / function generateRobot (conf: Object): Robot //

Maintenant, si je commence à écrire un appel à cette fonction dans FlashDevelop (mon IDE de choix), je vois ceci:

Bien sûr, il est un peu pénible de garder cette mise à jour manuellement, mais dans de nombreux cas, cela en vaut la peine.


Conclusion

Je ne prétends pas que vous devriez utiliser un objet de configuration pour chaque fonction vous créez à partir de maintenant; il suffit de penser à cela comme un autre outil utile dans votre arsenal.

Personnellement, je trouve que c'est un modèle particulièrement utile lorsque je construis le premier brouillon d'un ensemble de classes qui doivent toutes fonctionner ensemble. La flexibilité supplémentaire d'un conf me donne beaucoup plus de flexibilité, me permettant de parcourir toutes les fonctions et de changer leur façon de s’appeler, sans craindre de rompre le code en insérant ou en supprimant un paramètre.

Rappelez-vous les avantages:

  • Il est facile d'ajouter et de supprimer des paramètres (à chaque extrémité).
  • Il est facile de définir les valeurs par défaut.
  • Vous n'avez pas à vous soucier de l'ordre des paramètres.

Cependant, il existe des inconvénients à utiliser des objets simples comme le mien - surtout si vous le faites dans un projet ayant dépassé l'étape du prototypage. Découvrez les excellents commentaires ci-dessous pour plus de détails!