Que signifie injecter des données (vs comportement) dans un constructeur de classe, et pourquoi est-ce considéré comme une mauvaise pratique?


10

Je lis le livre "Learning TypeScript" de Remo Jansen. Dans une section, l'auteur décrit comment créer un cadre MVC de preuve de concept très simple, y compris comment créer la Modelclasse et dit ce qui suit:

Un modèle doit être fourni avec l'URL du service Web qu'il consomme. Nous allons utiliser un décorateur de classe nommé ModelSettings pour définir l'URL du service à consommer. Nous pourrions injecter l'URL du service via son constructeur, mais il est considéré comme une mauvaise pratique d'injecter des données (par opposition à un comportement) via un constructeur de classe .

Je ne comprends pas cette dernière phrase. En particulier, je ne comprends pas ce que signifie "injecter des données". Il me semble que dans presque toutes les introductions aux classes JavaScript utilisant des exemples simplifiés, des données sont introduites ("injectées"?) Dans le constructeur via ses paramètres. Par exemple:

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

Je pense certainement à des namedonnées, pas à un comportement, et elles sont universellement incluses dans ce type d'exemple comme paramètre constructeur, et il n'est jamais fait mention que c'est une mauvaise pratique. Je suppose donc que je comprends mal quelque chose dans la citation ci-dessus, soit ce que l'on entend par "données" ou par "injecter" ou autre chose.

Vos réponses pourraient inclure des explications sur quand, où, comment et pourquoi utiliser des décorateurs en JavaScript / TypeScript, car je soupçonne fortement que le concept est intimement lié à la compréhension que je recherche. Cependant, plus important encore, je veux comprendre plus généralement ce que l'on entend par injection de données via un constructeur de classe et pourquoi c'est mauvais.


Pour donner plus de contexte à la citation ci-dessus, voici la situation: une Modelclasse est créée qui, dans cet exemple, sera utilisée pour créer des modèles de bourse, un pour NASDAQ et un pour NYSE. Chaque modèle nécessite le chemin du service Web ou du fichier de données statiques qui fournira les données brutes. Le livre indique qu'un décorateur doit être utilisé pour ces informations, plutôt qu'un paramètre constructeur, conduisant à ce qui suit:

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

Je ne comprends tout simplement pas pourquoi je devrais ajouter l'URL du service via le décorateur plutôt que simplement comme paramètre pour le constructeur, par exemple

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

Je vous suggère de faire une recherche rapide sur google sur l' injection de dépendance . Ce n'est pas le bon forum pour poser cette question. :)
toskv

1
Je prendrai votre réponse à cœur, mais j'ai cherché sur Google et j'ai rencontré des discussions sur l'injection de dépendance. «Injection de dépendance» et «injection de données» font-elles référence à la même chose? De plus, j'ai eu l'impression que "l'injection de dépendance" est une "bonne chose" (ou au moins une "chose alternative"), alors que la discussion sur "l'injection de données" dans la citation que j'ai fournie donne l'impression que c'est "une mauvaise chose" .

L'injection de dépendance et l'injection de données sont deux choses différentes. le 1er est un principe de conception tandis que le 2e est un type d'attaque. Si vous voulez un terme de recherche plus clair, essayez "inversion de contrôle". Il est un peu plus large mais permet également de brosser un tableau plus clair.
toskv

1
Les attaques par "injection de données" sont, je crois, un animal très différent de ce dont parle l'auteur du livre cité lorsqu'il dit "injecter des données". C'est l'une des raisons pour lesquelles j'ai été frustré par les recherches Google à ce sujet. Même si j'ai besoin de mieux comprendre, par exemple les principes SOLIDES, je ne comprends pas comment fournir un "nom" comme paramètre à un constructeur "Personne" est normal et OK mais fournir un "serviceUrl" comme paramètre à un "Modèle" constructeur est inapproprié, ou en quoi il est même différent de l'exemple "nom" / "Personne".

7
Je pense que Remo se trompe. Les paramètres sont des données, quoi qu'il dise. Les données qui sont injectées ont toujours un type, et tous les types dans les langages orientés objet ont un comportement quelconque.
Robert Harvey

Réponses:


5

Je donnerai à l'auteur le bénéfice du doute et c'est peut-être la façon dont les choses sont pour Typescript, mais sinon dans d'autres environnements, c'est une affirmation totalement infondée qui ne devrait pas être prise au sérieux.

Du haut de ma tête, je peux penser à une variété de situations où le passage de données via le constructeur est bon, certains qui sont neutres, mais aucun où c'est mauvais.

Si une classe particulière dépend d'une donnée particulière pour être dans un état valide et s'exécuter correctement, il est parfaitement logique d'exiger ces données dans le constructeur. Une classe représentant un port série pourrait prendre le nom du port, un objet fichier pourrait nécessiter le nom de fichier, un canevas de dessin nécessitant sa résolution, etc. doit être surveillé et vérifié. Sinon, vous ne pouvez vérifier qu'à l'instanciation de l'objet et supposer ensuite son fonctionnement pour la plupart. Les auteurs affirment que cette situation bénéfique est impossible.

De plus, la décision d'interdire le passage de données dans un constructeur rend également pratiquement tous les objets immuables impossibles. Les objets immuables ont une variété d'avantages dans de nombreuses situations, et tous ces éléments seraient rejetés avec la politique de l'auteur.

Même si vous voulez des objets modifiables, quelle est cette mauvaise pratique:

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

en faveur de:

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

L'auteur pense-t-il vraiment que la première est une mauvaise pratique et que je devrais toujours choisir l'option 2? Je pense que c'est un discours fou.

Donc, puisque je n'ai pas le livre, et que je ne le lirais pas de toute façon, même si je le faisais, je verrais cette déclaration et à peu près n'importe quelle déclaration générale à ce stade avec beaucoup de suspicion.


Merci beaucoup pour votre discussion. Ce que vous dites me "sent bon", surtout lorsque vous donnez l'exemple Rectangle. Je me demande toujours si l'auteur fait une distinction entre les données requises pour la classe et pour chaque instance de la classe. Cependant, je ne pense pas que le projet décrit dans le livre soit suffisamment approfondi pour clarifier cela. En guise de note, votre réponse m'a envoyé sur une première enquête sur l'immuabilité des objets, quelle que soit la manière dont elle se rapporte ou non à ma question d'origine, alors merci aussi!
Andrew Willems

0

Je pense que cela dépend du contexte du type de modèle qui est discuté ici. Je n'ai pas le livre de Remo, mais je suppose que le modèle est une sorte de modèle de service, qui doit récupérer les données d'un service Web distant. Si tel est le cas, étant un modèle de service Web, il est préférable de passer toutes les données requises comme arguments dans les méthodes du service Web, ce qui rend le service sans état.

Le service sans état présente plusieurs avantages, par exemple, toute personne lisant un appel de méthode de service n'a pas besoin de rechercher lors de la construction du service pour connaître les détails du service appelé. Tous les détails sont affichés dans les arguments utilisés dans l'appel de méthode (à l'exception de l'URL distante).


Oui, le modèle doit récupérer les données (éventuellement à partir d'un service Web distant comme vous le suggérez, mais dans le livre, il ne s'agit que d'une démonstration, il ne s'agit donc au départ que de données simulées codées directement en ligne). Je ne comprends pas votre suggestion de transmettre des données comme arguments "dans les méthodes du service Web". Je demandais à faire la différence entre le passage de données en tant que paramètres (1) pour un constructeur et (2) pour un décorateur. Vous semblez suggérer une troisième option, c'est-à-dire passer des données comme paramètres / arguments pour une méthode du service Web. Suis-je en train de manquer votre point?
Andrew Willems

0

Je devine.

Si j'entends «injecter du comportement, pas des données», je penserais, au lieu de faire ceci:

(Désolé pour l'exemple en pseudocode):

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

Pour faire ça:

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 cette façon, vous pouvez toujours changer le comportement du bruit, le rendre aléatoire, dépendant d'une variable interne ...

Je pense que tout tourne autour de la règle «favoriser le composite par rapport à l'héritage» Ce qui est une excellente règle, je dois dire.

Cela NE SIGNIFIE PAS que vous ne pouvez pas «injecter» le nom à l'objet «Personne», évidemment, car ce nom est purement des données commerciales. Mais dans l'exemple que vous donnez, le service Web, l'URL est quelque chose que vous avez besoin de générer quelque chose d'une certaine manière qui relie un service. C'est en quelque sorte un comportement: si vous injectez l'URL, vous injectez les «données» nécessaires pour créer un «comportement», dans ce cas, il est préférable de rendre le comportement à l'extérieur et de l'injecter prêt à être utilisé: injectez plutôt une injection d'URL une connexion utilisable ou un constructeur de connexion utilisable.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.