Was bedeutet es, Daten (vs Verhalten) in einen Klassenkonstruktor einzufügen, und warum wird dies als schlechte Praxis angesehen?


10

Ich lese das Buch "Learning TypeScript" von Remo Jansen. In einem Abschnitt beschreibt der Autor, wie ein sehr einfaches Proof-of-Concept-MVC-Framework erstellt wird, einschließlich der Erstellung der ModelKlasse, und sagt Folgendes:

Ein Modell muss mit der URL des Webdienstes versehen sein, den es verwendet. Wir werden einen Klassendekorateur namens ModelSettings verwenden, um die URL des zu konsumierenden Dienstes festzulegen. Wir könnten die Service-URL über ihren Konstruktor einfügen, aber es wird als schlechte Praxis angesehen, Daten (im Gegensatz zu einem Verhalten) über einen Klassenkonstruktor einzufügen .

Ich verstehe diesen letzten Satz nicht. Insbesondere verstehe ich nicht, was es bedeutet, "Daten zu injizieren". Es scheint mir, dass in fast allen Einführungen in JavaScript-Klassen anhand stark vereinfachter Beispiele Daten über ihre Parameter in den Konstruktor eingeführt ("injiziert"?) Werden. Zum Beispiel:

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

Ich denke sicherlich an nameDaten, nicht an Verhalten, und es ist allgemein in dieser Art von Beispiel als Konstruktorparameter enthalten, und es wird nie erwähnt, dass dies eine schlechte Praxis ist. Ich gehe daher davon aus, dass ich etwas im obigen Zitat falsch verstehe, entweder was mit "Daten" oder "Einspritzen" oder etwas anderem gemeint ist.

Ihre Antworten könnten Erklärungen enthalten, wann, wo, wie und warum Dekoratoren in JavaScript / TypeScript verwendet werden, da ich stark vermute, dass das Konzept eng mit dem von mir gesuchten Verständnis verbunden ist. Noch wichtiger ist jedoch, dass ich allgemeiner verstehen möchte, was unter dem Einfügen von Daten über einen Klassenkonstruktor zu verstehen ist und warum dies schlecht ist.


Um dem obigen Zitat mehr Kontext zu geben, ist dies die Situation: Es Modelwird eine Klasse erstellt, die in diesem Beispiel zum Erstellen von Börsenmodellen verwendet wird, eines für NASDAQ und eines für NYSE. Jedes Modell benötigt den Pfad des Webdienstes oder der statischen Datendatei, die die Rohdaten bereitstellen. Das Buch besagt, dass für diese Informationen ein Dekorateur anstelle eines Konstruktorparameters verwendet werden sollte, was zu Folgendem führt:

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

Ich habe nur nicht verstanden, warum ich die Service-URL über den Dekorator hinzufügen sollte, anstatt nur als Parameter für den Konstruktor, z

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

Ich würde vorschlagen, dass Sie bei Google eine schnelle Suche nach Abhängigkeitsinjektion durchführen . Dies ist nicht das richtige Forum, um diese Frage zu stellen. :)
toskv

1
Ich werde Ihre Antwort zu Herzen nehmen, aber ich habe Google durchsucht und bin auf Diskussionen über die Abhängigkeitsinjektion gestoßen. Beziehen sich "Abhängigkeitsinjektion" und "Dateninjektion" auf dasselbe? Außerdem hatte ich den Eindruck, dass "Abhängigkeitsinjektion" eine "gute Sache" (oder zumindest eine "alternative Sache") ist, während die Diskussion über "Dateninjektion" in dem von mir bereitgestellten Zitat eine "schlechte Sache" erscheinen lässt. .

Abhängigkeitsinjektion und Dateninjektion sind zwei verschiedene Dinge. Das erste ist ein Konstruktionsprinzip, während das zweite eine Art Angriff ist. Wenn Sie einen klareren Suchbegriff wünschen, versuchen Sie "Umkehrung der Kontrolle". Es ist etwas breiter, hilft aber auch dabei, ein klareres Bild zu zeichnen.
toskv

1
"Dateninjektions" -Angriffe sind meines Erachtens ein ganz anderes Tier als das, worüber der Autor des zitierten Buches spricht, wenn er "Dateninjektion" sagt. Dies ist einer der Gründe, warum ich mit Google-Suchanfragen frustriert war. Selbst wenn ich verstehen muss, z. B. SOLID-Prinzipien besser, verstehe ich nicht, wie normal und in Ordnung es ist, einem "Person" -Konstruktor einen "Namen" als Parameter bereitzustellen, aber einem "Modell" eine "serviceUrl" als Parameter bereitzustellen. Der Konstruktor ist unangemessen oder unterscheidet sich sogar vom Beispiel "Name" / "Person".

7
Ich denke, Remo irrt sich. Parameter sind Daten, egal was er sagt. Eingefügte Daten haben immer einen Typ, und alle Typen in objektorientierten Sprachen weisen ein Verhalten auf.
Robert Harvey

Antworten:


5

Ich werde dem Autor den Vorteil des Zweifels geben, und vielleicht ist das bei Typescript so, aber ansonsten ist dies in anderen Umgebungen eine völlig unbegründete Behauptung, die nicht ernst genommen werden sollte.

Auf den ersten Blick kann ich mir eine Vielzahl von Situationen vorstellen, in denen die Übergabe von Daten über den Konstruktor gut ist, einige davon neutral, aber keine schlecht.

Wenn eine bestimmte Klasse von einem bestimmten Datenelement abhängt, um in einem gültigen Zustand zu sein und ordnungsgemäß ausgeführt zu werden, ist es durchaus sinnvoll, diese Daten im Konstruktor anzufordern. Eine Klasse, die eine serielle Schnittstelle darstellt, könnte den Portnamen annehmen, ein Dateiobjekt könnte den Dateinamen erfordern, eine Zeichenfläche, deren Auflösung erforderlich ist usw. Wenn Sie die Daten nicht im Konstruktor übergeben, ist es möglich, dass sich das Objekt in einem ungültigen Zustand befindet muss beobachtet und überprüft werden. Andernfalls können Sie nur bei der Objektinstanziierung prüfen und anschließend davon ausgehen, dass sie größtenteils funktioniert. Die Autoren behaupten, dass diese vorteilhafte Situation unmöglich ist.

Darüber hinaus macht die Entscheidung, die Weitergabe von Daten in einem Konstruktor zu verbieten, praktisch alle unveränderlichen Objekte unmöglich. Unveränderliche Objekte haben in vielen Situationen eine Vielzahl von Vorteilen, und all diese werden mit den Richtlinien des Autors verworfen.

Selbst wenn veränderbare Objekte das sind, was Sie wollen, wie ist diese schlechte Praxis:

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

zugunsten:

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

Hält der Autor das erste wirklich für eine schlechte Praxis und ich sollte immer mit Option 2 gehen? Ich denke, das ist verrücktes Gerede.

Da ich das Buch nicht habe und es auch dann nicht lesen würde, wenn ich es tun würde, würde ich diese Aussage und so ziemlich jede allgemeine Aussage an dieser Stelle mit einem erheblichen Misstrauen betrachten.


Vielen Dank für Ihre Diskussion. Was Sie sagen, "riecht richtig" für mich, besonders wenn Sie das Beispiel "Rechteck" geben. Ich frage mich immer noch, ob der Autor zwischen den für die Klasse erforderlichen Daten und den für jede Instanz der Klasse erforderlichen Daten unterscheidet. Ich denke jedoch nicht, dass das Projekt, das das Buch beschreibt, wirklich tief genug geht, um dies zu verdeutlichen. Als Randnotiz hat mich Ihre Antwort zu einer ersten Untersuchung der Unveränderlichkeit von Objekten geschickt, unabhängig davon, wie sehr sie sich auf meine ursprüngliche Frage bezieht oder nicht. Vielen Dank auch dafür!
Andrew Willems

0

Ich denke, es hängt vom Kontext ab, welche Art von Modell hier diskutiert wird. Ich habe Remos Buch nicht, aber ich denke, dass das Modell eine Art Servicemodell ist, das die Daten von einem Remote-Webdienst abrufen muss. Wenn dies der Fall ist, ist es als Webdienstmodell besser, alle erforderlichen Daten als Argumente in den Methoden des Webdienstes zu übergeben, wodurch der Dienst zustandslos wird.

Ein zustandsloser Dienst hat mehrere Vorteile. Beispielsweise muss jeder, der einen Dienstmethodenaufruf liest, nicht nachschlagen, wenn der Dienst erstellt wird, um die Details des aufgerufenen Dienstes herauszufinden. Alle Details werden in den Argumenten angezeigt, die im Methodenaufruf verwendet werden (mit Ausnahme der Remote-URL).


Ja, das Modell muss die Daten abrufen (eventuell von einem Remote-Webdienst, wie Sie vorschlagen, aber in diesem Buch handelt es sich nur um eine Demo, sodass es sich zunächst nur um direkt inline codierte Scheindaten handelt). Ich verstehe Ihren Vorschlag, Daten als Argumente "in den Methoden des Webdienstes" zu übergeben, nicht. Ich fragte nach der Unterscheidung zwischen der Übergabe von Daten als Parameter (1) für einen Konstruktor und (2) für einen Dekorateur. Sie scheinen eine dritte Option vorzuschlagen, dh die Übergabe von Daten als Parameter / Argumente für eine Methode des Webdienstes. Vermisse ich deinen Standpunkt?
Andrew Willems

0

Einfach raten.

Wenn ich "Injektionsverhalten, nicht Daten" höre, würde ich darüber nachdenken, anstatt dies zu tun:

(Entschuldigung für das Beispiel im Pseudocode):

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

Um dies zu tun:

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

Auf diese Weise können Sie das Verhalten des Rauschens immer ändern, es zufällig machen, abhängig von einer inneren Variablen ...

Ich denke, es geht nur um die Regel "Verbund gegenüber Vererbung bevorzugen". Das ist eine großartige Regel, muss ich sagen.

Dies bedeutet NICHT, dass Sie den Namen nicht in das Objekt "Person" "einfügen" können, da es sich bei diesem Namen offensichtlich um reine Geschäftsdaten handelt. Aber in dem Beispiel , das Sie geben, den Web - Service, ist die URL etwas , das Sie brauchen , um etwas zu erzeugen , irgendwie , dass ein Dienst verbindet. Das ist irgendwie ein Verhalten: Wenn Sie die URL einfügen, fügen Sie die 'Daten' ein, die zum Erstellen eines 'Verhaltens' erforderlich sind. In diesem Fall ist es besser, das Verhalten nach außen zu verschieben und einsatzbereit zu injizieren: Injizieren Sie stattdessen eine URL-Injektion eine verwendbare Verbindung oder ein verwendbarer Verbindungsaufbau.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.