Cosa significa iniettare dati (comportamento vs) 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 proof-of-concept molto semplice che include come creare la classe Model e dice quanto segue:

A model needs to be provided with the URL of the web service that it consumes. We are going to use a class decorator named ModelSettings to set the URL of the service to be consumed. We could inject the service URL via its constructor, but it is considered a bad practice to inject data (as opposed to a behavior) via a class constructor.

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

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

Penso certamente a name come dati, non come comportamento, ed è universalmente incluso in questo tipo di esempio come parametro costruttore, e non c'è mai alcuna menzione che questa sia una cattiva pratica. Suppongo quindi di fraintendere qualcosa nella citazione sopra, sia che cosa si intenda per "dati" o per "inietti" o qualcos'altro.

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

Per dare più contesto alla citazione sopra, questa è la situazione: viene creata una classe Model che, in questo esempio, verrà utilizzata per creare modelli di borsa, una per NASDAQ e una per 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 questa informazione, piuttosto che un parametro costruttore, che porta a quanto segue:

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

Semplicemente 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) {...
    
posta Andrew Willems 02.06.2016 - 21:56
fonte

3 risposte

4

Darò all'autore il beneficio del dubbio e forse questo è il modo in cui le cose sono per Typescript, ma per il resto in altri ambienti è una affermazione totalmente infondata che non dovrebbe essere presa sul serio.

In cima alla mia testa, posso pensare a una varietà di situazioni in cui i dati di passaggio tramite costruttore sono buoni, altri neutri, ma nessuno in cui è cattivo.

Se una determinata classe dipende da una particolare parte di dati per essere in uno stato valido e funzionare correttamente, è perfettamente logico richiedere i dati nel costruttore. Una classe che rappresenta una porta seriale potrebbe prendere il nome della porta, un oggetto file potrebbe richiedere il nome file, una tela di disegno che richiede la sua risoluzione, ecc. A meno che non si passino i dati nel costruttore, è possibile che l'oggetto sia in uno stato non valido deve essere guardato e controllato Altrimenti è possibile controllare solo all'istanza dell'oggetto e successivamente assumerne il funzionamento per la maggior parte. Gli autori affermano che rende impossibile questa situazione vantaggiosa.

Inoltre, decidere di vietare il passaggio dei dati in un costruttore rende praticamente impossibile tutti gli oggetti immutabili. Gli oggetti immutabili hanno una varietà di benefici in molte situazioni, e tutti questi sarebbero buttati fuori con la politica dell'autore.

Anche se gli oggetti mutabili sono ciò che vuoi, com'è 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 andare con l'opzione 2? Penso che sia un pazzo parlare.

Quindi, dal momento che non ho il libro, e non lo leggerei comunque anche se lo facessi, vedrei questa affermazione e praticamente qualsiasi affermazione generale a questo punto con una quantità significativa di sospetti.

    
risposta data 12.06.2016 - 09:12
fonte
0

Penso che dipenda dal contesto che tipo di modello viene discusso qui. Non ho il libro di Remo, ma suppongo che il modello sia una sorta di modello di servizio, che ha bisogno di 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.

I servizi stateless hanno diversi vantaggi, ad esempio, chiunque legga una chiamata al metodo di servizio non debba cercare quando il servizio è costruito per scoprire i dettagli del servizio chiamato. Tutti i dettagli sono mostrati negli argomenti usati nella chiamata al metodo (eccetto l'URL remoto).

    
risposta data 12.06.2016 - 04:58
fonte
0

Solo supposizioni.

Se dovessi "iniettare il comportamento, non i dati", ci penserei, invece di fare questo:

(scusa per l'esempio in pseudocodice):

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

Per fare ciò:

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 basato sulla regola del "favor composite over inherit". Questa è una grande regola, devo dire.

Questo NON SIGNIFICA che non è possibile "iniettare" il nome sull'oggetto "Persona", ovviamente, perché quel nome è puramente commerciale. Ma nell'esempio che tu dai, il servizio web, l'URL è qualcosa che devi generare qualcosa in qualche modo che connette un servizio. Che in qualche modo è un comportamento: se si inietta l'URL, si iniettano i "dati" necessari per costruire un "comportamento", quindi in tal caso è meglio rendere il comportamento esterno e iniettarlo pronto per essere usato: invece iniettare un URL inietta una connessione utilizzabile o un connectionbuilder utilizzabile.

    
risposta data 09.01.2019 - 18:53
fonte

Leggi altre domande sui tag