Cosa significa iniettare dati (rispetto al comportamento) in un costruttore di classi, e perché è considerata una cattiva pratica?


10

Sto leggendo il libro "Learning TypeScript" di Remo Jansen. In una sezione l'autore descrive come creare un framework MVC di prova del concetto molto semplice, incluso come creare la Modelclasse e dice quanto segue:

È necessario fornire un modello con l'URL del servizio Web che consuma. Useremo un decoratore di classe chiamato ModelSettings per impostare l'URL del servizio da consumare. Potremmo iniettare l'URL del servizio tramite il suo costruttore, ma è considerata una cattiva pratica iniettare dati (al contrario di un comportamento) tramite un costruttore di classi .

Non capisco quest'ultima frase. In particolare, non capisco cosa significhi "iniettare dati". Mi sembra che in quasi tutte le introduzioni alle classi JavaScript utilizzando esempi troppo semplificati, i dati vengono introdotti ("iniettati"?) Nel costruttore tramite i suoi parametri. Per esempio:

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

Penso certamente nameai dati, non al comportamento, ed è universalmente incluso in questo tipo di esempio come parametro del costruttore, e non si fa mai menzione del fatto che si tratta di una cattiva pratica. Suppongo quindi di aver frainteso qualcosa nella citazione sopra, o cosa si intende per "dati" o per "iniettare" o qualcos'altro.

Le tue risposte potrebbero includere spiegazioni su quando, dove, come e perché utilizzare i decoratori in JavaScript / TypeScript, poiché sospetto fortemente che il concetto sia intimamente connesso alla comprensione che cerco. Tuttavia, cosa ancora più importante, voglio capire più in generale cosa si intende iniettando dati tramite un costruttore di classi e perché ciò non va bene.


Per dare più contesto alla quotazione sopra, questa è la situazione: Modelviene creata una classe che, in questo esempio, verrà utilizzata per creare modelli di borsa, uno per il NASDAQ e uno per il NYSE. Ogni modello richiede il percorso del servizio Web o del file di dati statici che fornirà i dati non elaborati. Il libro afferma che un decoratore dovrebbe essere usato per queste informazioni, piuttosto che un parametro del costruttore, portando a quanto segue:

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

Non ho capito perché dovrei aggiungere l'URL del servizio tramite il decoratore piuttosto che semplicemente come parametro per il costruttore, ad es.

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

Ti suggerirei di fare una rapida ricerca su google sull'iniezione delle dipendenze . Questo non è il forum corretto per porre questa domanda. :)
toskv,

1
Prenderò a cuore la tua risposta, ma ho cercato su Google e ho incontrato discussioni sull'iniezione delle dipendenze. "Iniezione delle dipendenze" e "Iniezione dei dati" si riferiscono alla stessa cosa? Inoltre, è stata la mia impressione che "iniezione di dipendenza" sia una "cosa buona" (o almeno una "cosa alternativa"), mentre la discussione di "iniezione di dati" nella citazione che ho fornito fa sembrare una "cosa cattiva" .

Iniezione di dipendenza e iniezione di dati sono 2 cose diverse. il primo è un principio progettuale mentre il secondo è un tipo di attacco. Se si desidera un termine di ricerca più chiaro, provare "inversione di controllo". È un po 'più ampio, ma aiuta anche a dipingere un'immagine più chiara.
Toskv,

1
Gli attacchi "data injection" sono, credo, un animale molto diverso da quello di cui parla l'autore del libro citato quando dice "inject data". Questo è uno dei motivi per cui sono stato frustrato dalle ricerche su Google su questo. Anche se ho bisogno di capire, ad es. Meglio i principi SOLID, non capisco come fornire un "nome" come parametro a un costruttore "Persona" sia normale e OK ma fornire un "serviceUrl" come parametro a un "Modello" Il costruttore non è appropriato o come è persino diverso dall'esempio "nome" / "Persona".

7
Penso che Remo si sbagli. I parametri sono dati, indipendentemente da ciò che dice. I dati che vengono iniettati hanno sempre un tipo e tutti i tipi nei linguaggi orientati agli oggetti hanno un comportamento di qualche tipo.
Robert Harvey,

Risposte:


5

Darò all'autore il beneficio del dubbio e forse è così che vanno le cose per Typescript, ma per il resto in altri ambienti è un'affermazione totalmente priva di fondamento che non dovrebbe essere presa sul serio.

Dalla parte superiore della mia testa, riesco a pensare a una varietà di situazioni in cui il passaggio di dati tramite il costruttore è buono, alcune che sono neutrali, ma nessuna in cui è negativa.

Se una particolare classe dipende da un particolare dato per essere in uno stato valido ed essere eseguita correttamente, ha perfettamente senso richiedere quei dati nel costruttore. Una classe che rappresenta una porta seriale potrebbe prendere il nome della porta, un oggetto file potrebbe richiedere il nome file, un'area di disegno che richieda la sua risoluzione, ecc. A meno che non si passino i dati nel costruttore, è possibile che l'oggetto non sia valido deve essere controllato e verificato. Altrimenti puoi controllare solo all'istanza dell'oggetto e successivamente assumere che funzioni per la maggior parte. Gli autori sostengono che questa situazione benefica è impossibile.

Inoltre, decidere di vietare il passaggio di dati in un costruttore rende praticamente impossibile qualsiasi oggetto immutabile. Gli oggetti immutabili hanno una varietà di vantaggi in molte situazioni, e tutti questi sarebbero eliminati con la politica dell'autore.

Anche se gli oggetti mutabili sono ciò che vuoi, come è questa cattiva pratica:

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

a favore di:

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

L'autore pensa davvero che la prima sia una cattiva pratica e dovrei sempre scegliere l'opzione 2? Penso che sia un discorso folle.

Quindi, dal momento che non ho il libro, e non lo leggerei neanche se lo facessi, vedrei quell'affermazione e praticamente qualsiasi affermazione generale in esso a questo punto con un notevole sospetto.


Grazie mille per la tua discussione. Quello che dici "ha un buon odore" per me, specialmente quando dai l'esempio di Rectangle. Mi chiedo ancora se l'autore stia facendo una distinzione tra i dati richiesti per la classe rispetto a ogni istanza della classe. Tuttavia, non penso che il progetto descritto nel libro sia davvero abbastanza approfondito da chiarirlo. Come nota a margine, la tua risposta mi ha inviato una prima indagine sull'immutabilità degli oggetti, per quanto riguardi o meno la mia domanda originale, quindi grazie anche per quello!
Andrew Willems,

0

Penso che dipenda dal contesto che tipo di modello viene discusso qui. Non ho il libro di Remo, ma immagino che il modello sia una specie di modello di servizio, che deve recuperare i dati da un servizio web remoto. In tal caso, essendo un modello di servizio Web, è meglio passare tutti i dati richiesti come argomenti nei metodi del servizio Web, rendendo il servizio senza stato.

Il servizio senza stato presenta numerosi vantaggi, ad esempio, chiunque legga una chiamata al metodo di servizio non deve cercare quando il servizio è costruito per scoprire i dettagli del servizio chiamato. Tutti i dettagli sono mostrati negli argomenti utilizzati nella chiamata al metodo (tranne l'URL remoto).


Sì, il modello deve recuperare i dati (eventualmente da un servizio Web remoto come da te suggerito, ma nel libro questa è solo una demo, quindi inizialmente sono solo dati fittizi codificati direttamente in linea). Non capisco il tuo suggerimento sul passaggio di dati come argomenti "nei metodi del servizio web". Stavo chiedendo di differenziare i dati di passaggio come parametri (1) per un costruttore contro (2) per un decoratore. Sembra che tu stia suggerendo una terza opzione, ovvero passare i dati come parametri / argomenti per un metodo del servizio web. Mi manca il tuo punto?
Andrew Willems,

0

Tiravo a indovinare.

Se ascolto "iniettare comportamenti, non dati", ci penserei, invece di fare questo:

(Ci scusiamo per l'esempio in pseudocodice):

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

Per farlo:

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())
  }
}

In questo modo puoi sempre modificare il comportamento del rumore, renderlo casuale, dipendente da una variabile interna ...

Penso che sia tutto incentrato sulla regola del "favore composito sull'ereditarietà". Quale è una grande regola, devo dire.

Ciò NON SIGNIFICA che non è possibile "iniettare" il nome nell'oggetto "Persona", ovviamente, poiché tale nome è puramente dati aziendali. Ma l'esempio che si dà, il servizio web, l'URL è qualcosa che è necessario per generare qualcosa in qualche modo che si connette il servizio. Questo in qualche modo è un comportamento: se si inserisce l'URL, si immettono i 'dati' necessari per creare un 'comportamento', quindi in tal caso è meglio rendere il comportamento esterno e iniettarlo pronto per essere utilizzato: Iniettare invece un URL iniettare una connessione utilizzabile o un generatore di connessioni utilizzabile.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.