¿Qué es un objeto de configuración y por qué molestarse en usarlo?

Es un dolor tener que cambiar los parámetros de una función; tienes que cambiar cada otra llamada a esa función para evitar errores. Pero puede solucionar esto usando solo un parámetro: un objeto de configuración.


Lo que parece

Aquí hay un ejemplo tonto de una función para crear un robot:

 function generateRobot (arms: int, personality: String): Robot var robot: Robot = new Robot (); para (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");

Ahora, aquí está el mismo ejemplo, usando un objeto de configuración:

 function generateRobot (conf: Object): Robot var robot: Robot = new Robot (); para (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");

He resaltado las líneas que requieren cambiar; Se puede ver que no hay mucha diferencia..


Por qué molestarse?

Entonces, si casi no hay diferencia, ¿por qué nos molestaríamos en hacerlo de la segunda manera? Después de todo, en realidad hace que la función sea un poco más difícil de usar; mientras que antes, nuestro IDE podría proporcionarnos esta información sobre los parámetros de la función esperada:

... ahora solo nos puede dar esto:

Supongamos que desea agregar un par de parámetros más: uno que especifique el material a usar y otro para especificar qué color debe ser su láser. Eso no es demasiado difícil, en cualquier caso:

 function generateRobot (arms: int, personalidad: String, material: String, laserColor: String): Robot var robot: Robot = new Robot (); para (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 var robot: Robot = new Robot (); para (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");

Hasta ahora, todavía no hay mucha diferencia. ¿Qué pasa si quieres que tus robots tengan todos los láseres rojos por defecto? Simple de nuevo. Sin un objeto de configuración, solo necesita cambiar la firma del método (la función línea), y luego puede eliminar el último argumento de la llamada de función:

 function generateRobot (arms: int, personality: String, material: String, laserColor: String = "red"): Robot // esto es todo igual generateRobot (2, true, "steel"); // Quité el último argumento

Con un objeto de configuración, es un poco más complicado, aunque no mucho:

 function generateRobot (conf: Object): Robot if (! conf.laserColor) conf.laserColor = "red";  var robot: Robot = nuevo Robot (); para (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

Bueno. Ahora supongamos que encuentras que estás configurando a casi todos tus robots para que sean malvados (quiero decir, ¿por qué no?), Así que en realidad es un tipo de dolor escribir un parámetro como "malvado" cada vez. Naturalmente, desea establecer "mal" como el valor predeterminado, pero no hacer desea establecer un material por defecto.

La única forma en que puede hacerlo, con un conjunto regular de parámetros de función, es cambiar el orden de personalidad y material parámetros:

 function generateRobot (arms: int, material: String, personalidad: String = "evil", laserColor: String = "red"): Robot 

Ah, pero ahora tienes que cambiar el orden de la ronda de argumentos en cada llamada a una sola función!

 generateRobot (2, "evil", "steel"); // ya no funciona

Un objeto de configuración no le da este problema. Echale un vistazo:

 function generateRobot (conf: Object): Robot if (! conf.laserColor) conf.laserColor = "red";  if (! conf.personality) conf.personality = "evil" // esto es lo mismo generateRobot (arms: 2, material: "steel"); // ¿No hay parámetro de "personalidad"? No hay problema!

¡Ordenado! Todo tu viejo generateRobot () las llamadas a funciones continuarán funcionando, pero puede crear nuevas llamadas que no molesten en especificar personalidad.

Incluso puede decidir deshacerse de la personalidad parámetro en conjunto:

 function generateRobot (conf: Object): Robot if (! conf.laserColor) conf.laserColor = "red";  if (! conf.personality) conf.personality = "evil" var robot: Robot = new Robot (); para (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 versión anterior de la función no se refiere a conf.personalidad en absoluto, pero no recibirá un error si todavía tiene llamadas como esta:

 generateRobot (brazos: 2, personalidad: "mal", material: "acero");

Por supuesto, puede obtener algunos usuarios confundidos si tiene llamadas como esta:

 generateRobot (brazos: 2, personalidad: "bueno", material: "acero");

... ya que todos los robots son ahora malvados. Pero al menos el código se compilará..

Por el mismo motivo, puede cambiar el orden de los argumentos sin importarlos en absoluto, e incluso agregar nuevos parámetros que todavía no hacen nada:

 generateRobot (material: "steel", laserColor: "green", arms: 2, voice: "Mr. T");

Haciendo que sea más fácil establecer valores predeterminados

El código para establecer los valores predeterminados es fácil de entender hasta el momento, pero será muy molesto de extender si necesitamos tener muchos parámetros:

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

Vamos a escribir un código más general para enfrentarlo:

 var por defecto: Objeto = laserColor: rojo, personalidad: "mal" para (var clave: Cadena en por defecto) if (! conf [key]) conf [key] = por defecto [key]; 

Ese para El bucle puede ser un poco confuso, así que lo desglosaré. Primero, mira esto:

 para (var key: cadena en los valores predeterminados) trace (key); 

Esto es un para ... en bucle, que dará salida a los nombres de las teclas dentro de la defecto objeto:

 personalidad laser color

A continuación, mira esta línea:

 trace (por defecto ["laserColor"]);

Esto dará salida rojo - es lo mismo que escribir traza (defaults.laserColor).

Siguiendo con eso, mira este ejemplo:

 var ejemplo: Object = demo: "test"; traza (ejemplo ["demo"]); traza (ejemplo ["foo"]);

¿Qué crees que esto producirá??

Bien, ejemplo ["demo"] es lo mismo que ejemplo.demo, que es igual a "prueba". Pero ejemplo.foo no existe, entonces ejemplo ["foo"] volverá nulo. Esto significa que !ejemplo ["foo"] (note el signo de exclamación) será equivalente a cierto.

Póngalo todo junto, y debería poder entender por qué funciona este código:

 var por defecto: Objeto = laserColor: rojo, personalidad: "mal" para (var clave: Cadena en por defecto) if (! conf [key]) conf [key] = por defecto [key]; 

Dame un grito en los comentarios si necesitas una mano.!

Quiero más!

Para una versión aún más rápida, prueba esto:

 function generateRobot (conf: Object = null): Robot var conf: Object = conf || ; valores predeterminados var: Objeto = laserColor: rojo, personalidad: "mal" para (clave var: cadena en valores predeterminados) conf [clave] = conf [clave] || valores predeterminados [clave]; 

El cambio en la Línea 1 (y la nueva Línea 2) significa que incluso la conf El objeto en sí es opcional, por lo que puede simplemente llamar generateRobot (). (Por supuesto, deberá cambiar el código para lidiar con los valores que actualmente no tienen valores predeterminados).


Ayudando al IDE a ayudarte

Como mencioné anteriormente, el IDE no puede ofrecerle sugerencias sobre qué parámetros espera una función, si esa función utiliza un objeto de configuración. Este es un gran inconveniente, ya que puede hacer que su código sea realmente difícil de usar; Tienes que recordar qué parámetros van en el conf objeto, así como todos sus nombres y tipos.

Pero aún podemos mostrar esta información al codificador cuando sea necesario; Solo tenemos que hacerlo manualmente, así:

 / ** * Generar un robot, basado en los parámetros dados. * @param conf Objeto de configuración. Espera: * brazos (int) Número de brazos que debe tener el robot. * Personalidad (String) Personalidad del robot. Puede ser "mal" o "bueno". Por defecto es "el mal". * material (Cuerda) De qué debe estar hecho el robot. Puede ser "acero" o "madera" en este momento. * laserColor (String) Color del láser del robot. El valor predeterminado es "rojo". * Voz (String) Estilizados vocales del robot. Actualmente no implementado. * @return El robot terminado. * / function generateRobot (conf: Object): Robot //

Ahora, si comienzo a escribir una llamada a esta función en FlashDevelop (mi IDE de elección), veo esto:

Claro, es un poco molesto mantener esto actualizado manualmente, pero en muchos casos vale la pena.


Conclusión

No estoy diciendo que debas usar un objeto de configuración para cada función creas de ahora en adelante; Solo piénsalo como otra herramienta útil en tu arsenal..

Personalmente, me parece un patrón particularmente útil cada vez que estoy creando el primer borrador de un conjunto de clases en las que todas deben trabajar juntas. La flexibilidad añadida de un conf me da mucha más flexibilidad, liberándome para hacer un zip de las diferentes funciones y cambiar la forma en que se llaman, sin preocuparse por romper el código insertando o eliminando un parámetro.

Recuerda los beneficios:

  • Es fácil agregar y eliminar parámetros (en cualquier extremo).
  • Es fácil establecer valores por defecto.
  • No tienes que preocuparte por el orden de los parámetros..

Sin embargo, hay inconvenientes en el uso de objetos simples como los que tengo, especialmente si lo hace en un proyecto que está más allá de la etapa de creación de prototipos. Echa un vistazo a los grandes comentarios a continuación para más detalles.!