¿Qué significa inyectar datos (vs comportamiento) en un constructor de clases, y por qué se considera una mala práctica?


10

Estoy leyendo el libro "Learning TypeScript" de Remo Jansen. En una sección, el autor describe cómo crear un marco MVC de prueba de concepto muy simple que incluye cómo crear la Modelclase y dice lo siguiente:

Se debe proporcionar un modelo con la URL del servicio web que consume. Vamos a utilizar un decorador de clase llamado ModelSettings para establecer la URL del servicio que se consumirá. Podríamos inyectar la URL del servicio a través de su constructor, pero se considera una mala práctica inyectar datos (a diferencia de un comportamiento) a través de un constructor de clase .

No entiendo esa última oración. En particular, no entiendo lo que significa "inyectar datos". Me parece que en casi todas las introducciones a las clases de JavaScript que usan ejemplos demasiado simplificados, los datos se introducen (¿"inyectados"?) En el constructor a través de sus parámetros. Por ejemplo:

class Person {
  constructor(name) {
    this.name = name;
  }
}

Ciertamente pienso nameen los datos, no como el comportamiento, y se incluye universalmente en este tipo de ejemplo como un parámetro constructor, y nunca se menciona que esta sea una mala práctica. Por lo tanto, supongo que estoy malentendiendo algo en la cita anterior, ya sea lo que se entiende por "datos" o por "inyectar" u otra cosa.

Sus respuestas podrían incluir explicaciones de cuándo, dónde, cómo y por qué usar decoradores en JavaScript / TypeScript, ya que sospecho que ese concepto está íntimamente relacionado con la comprensión que busco. Sin embargo, lo que es más importante, quiero comprender de manera más general qué se entiende por inyectar datos a través de un constructor de clases y por qué eso es malo.


Para dar más contexto a la cita anterior, esta es la situación: Modelse crea una clase que, en este ejemplo, se utilizará para crear modelos de bolsa, uno para NASDAQ y otro para NYSE. Cada modelo requiere la ruta del servicio web o el archivo de datos estáticos que proporcionará los datos sin procesar. El libro establece que se debe usar un decorador para esta información, en lugar de un parámetro constructor, lo que lleva a lo siguiente:

@ModelSettings("./data/nasdaq.json")
class NasdaqModel extends Model implements IModel {
  constructor(metiator : IMediator) {
    super(metiator);
  }
...
}

Simplemente no he entendido por qué debería agregar la URL del servicio a través del decorador en lugar de simplemente como un parámetro para el constructor, por ejemplo

constructor(metiator : IMediator, serviceUrl : string) {...

Te sugiero que hagas una búsqueda rápida en google sobre la inyección de dependencia . Este no es el foro correcto para hacer esta pregunta. :)
toskv

1
Tomaré en serio su respuesta, pero he buscado en google y he encontrado discusiones sobre la inyección de dependencia. ¿La "inyección de dependencia" y la "inyección de datos" se refieren a lo mismo? Además, he tenido la impresión de que la "inyección de dependencia" es una "cosa buena" (o al menos una "cosa alternativa"), mientras que la discusión sobre "inyección de datos" en la cita que proporcioné hace que parezca "algo malo" .

La inyección de dependencia y la inyección de datos son 2 cosas diferentes. el primero es un principio de diseño mientras que el segundo es un tipo de ataque. Si desea un término de búsqueda más claro, intente "inversión de control". Es un poco más amplio, pero también ayuda a pintar una imagen más clara.
toskv

1
Los ataques de "inyección de datos" son, creo, un animal muy diferente del que habla el autor del libro citado cuando dice "inyectar datos". Esa es una de las razones por las que me he sentido frustrado con las búsquedas de Google en esto. Incluso si necesito entender, por ejemplo, los principios SOLID mejor, no entiendo cómo proporcionar un "nombre" como parámetro a un constructor de "Persona" es normal y correcto, pero proporcionar un "serviceUrl" como parámetro a un "Modelo" el constructor es inapropiado, o cómo es incluso diferente del ejemplo de "nombre" / "Persona".

77
Creo que Remo está equivocado. Los parámetros son datos, no importa lo que él diga. Los datos que se inyectan siempre tienen un tipo, y todos los tipos en lenguajes orientados a objetos tienen algún tipo de comportamiento.
Robert Harvey

Respuestas:


5

Le daré al autor el beneficio de la duda y tal vez las cosas sean así para Typecript, pero de lo contrario, en otros entornos, esa es una afirmación totalmente infundada que no debe tomarse en serio.

Fuera de mi cabeza, puedo pensar en una variedad de situaciones donde pasar datos a través del constructor es bueno, algunos son neutrales, pero ninguno es malo.

Si una clase particular depende de un dato particular para estar en un estado válido y ejecutarse correctamente, tiene mucho sentido exigir esos datos en el constructor. Una clase que representa un puerto serie podría tomar el nombre del puerto, un objeto de archivo podría requerir el nombre del archivo, un lienzo de dibujo que requiera su resolución, etc. A menos que pase los datos en el constructor, es posible que pueda tener el objeto en un estado no válido que tiene que ser observado y verificado. De lo contrario, puede verificar solo en la instanciación de objetos y luego asumir que funciona en su mayor parte. Los autores afirman que esa situación beneficiosa es imposible.

Además, la decisión de prohibir el paso de datos en un constructor también hace que prácticamente todos los objetos inmutables sean imposibles. Los objetos inmutables tienen una variedad de beneficios en muchas situaciones, y todos estos se eliminarían con la política del autor.

Incluso si los objetos mutables son lo que quieres, ¿cómo es esta mala práctica?

var blah = new Rectangle(x,y,width,height);

a favor de:

var blah = new Rectangle();
blah.X = x;
blah.Y = y;
blah.Width = width;
blah.Height = height;

¿El autor realmente piensa que lo primero es una mala práctica y que siempre debería ir con la opción 2? Creo que es una locura hablar.

Entonces, como no tengo el libro, y no lo leería de todos modos, incluso si lo tuviera, vería esa declaración y casi cualquier declaración general en este momento con una gran cantidad de sospecha.


Muchas gracias por tu discusión. Lo que usted dice "huele bien" para mí, especialmente cuando da el ejemplo del Rectángulo. Todavía me pregunto si el autor está haciendo una distinción entre los datos requeridos para la clase y para cada instancia de la clase. Sin embargo, no creo que el proyecto que describe el libro realmente profundice lo suficiente como para aclararlo. Como nota al margen, su respuesta me envió a una investigación inicial de la inmutabilidad de los objetos, por mucho que se relacione o no con mi pregunta original, ¡así que gracias por eso también!
Andrew Willems

0

Creo que depende del contexto qué tipo de modelo se está discutiendo aquí. No tengo el libro de Remo, pero supongo que el modelo es un tipo de modelo de servicio, que necesita recuperar los datos de un servicio web remoto. Si ese es el caso, al ser un modelo de servicio web, es mejor pasar todos los datos necesarios como argumentos en los métodos del servicio web, haciendo que el servicio sea apátrida.

El servicio sin estado tiene varias ventajas, por ejemplo, cualquiera que lea una llamada al método de servicio no necesita buscar cuando el servicio se construye para conocer los detalles del servicio llamado. Todos los detalles se muestran en los argumentos que se utilizan en la llamada al método (excepto la url remota).


Sí, el modelo necesita recuperar los datos (eventualmente de un servicio web remoto como sugiere, pero en el libro esto es solo una demostración, por lo que inicialmente solo son datos simulados codificados directamente en línea). No entiendo su sugerencia sobre pasar datos como argumentos "en los métodos del servicio web". Estaba preguntando sobre la diferenciación entre pasar datos como parámetros (1) para un constructor versus (2) para un decorador. Parece sugerir una tercera opción, es decir, pasar datos como parámetros / argumentos para un método del servicio web. ¿Me estoy perdiendo tu punto?
Andrew Willems

0

Solo adivinando.

Si escucho 'inyectar comportamiento, no datos', pensaría en, en lugar de hacer esto:

(Perdón por el ejemplo en pseudocódigo):

class NoiseMaker{
  String noise;
  NoiseMaker(String noise){
     this.noise = noise;
  }
  void soNoise(){
    writeToOutput(noise)
  }
}

Para hacer esto:

interface Noise{
  String getAudibleNoise();
}

class PanicYell implements Noise{
   String getAudibleNoise(){
       return generateRandomYell();
   }
   .....
}



class WhiteNoise implements Noise{
   String getAudibleNoise(){
       return generateNoiseAtAllFrequences();
   }
   .....
}

class NoiseMaker{
  Noise noise;
  NoiseMaker(Noise noise){
     this.noise = noise;
  }
  void soNoise(){
    writeToOutput(noise.getAudibleNoise())
  }
}

De esta manera puede cambiar el comportamiento del ruido siempre, hacerlo aleatorio, dependiente de una variable interna ...

Creo que se trata de la regla 'favor compuesto sobre heredar'. Lo cual es una gran regla, debo decir.

Esto NO SIGNIFICA que no puede 'inyectar' el nombre al Objeto 'Persona', obviamente, porque ese nombre es puramente información comercial. Sin embargo, en el ejemplo que se da, el servicio web, la URL es algo que necesita para generar algo de alguna manera que se conecta un servicio. Eso de alguna manera es un comportamiento: si inyecta la URL, inyecta los 'datos' necesarios para construir un 'comportamiento', por lo que en ese caso es mejor hacer que el comportamiento esté fuera e inyectarlo listo para usar: en su lugar, inyecte una inyección de URL una conexión utilizable o un generador de conexiones utilizable.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.