Abhängigkeitsinjektion: Soll ich eine Fahrzeugklasse erstellen, die alle ihre Teile enthält?

10

Ich habe viele Autos in meiner C ++ - Anwendung, die alle in einem RaceTrack enthalten sind.

Jedes Auto besteht aus Hunderten von Teilen. Jeder Teil hängt von einem oder zwei anderen Teilen ab.

Ich habe viele SO-Fragen zu DI und Mark Seemanns Buch gelesen und es sieht so aus, als sollte ich keine Autoklasse definieren, nur um Autoteile zu halten, da alle Autoteile voneinander abhängen und diese Suppe von Teilen ein Auto ist. Habe ich recht?

Wenn ich also alle meine Rennwagen in die RaceTrack stecke, gibt es keine Autoeinheiten, sondern viele Autoteile, die voneinander abhängig sind und auf der Strecke fahren? Habe ich recht?

BEARBEITEN:

Ich habe lange Zeit Autoklassen programmiert, weil mir klar war, dass ich eine Autoklasse brauche, wenn ich eine Autologik programmiere. Aber mit DI ist es mir nicht so offensichtlich. Sie fragen sich immer noch, ob es für DI eine Redewendung ist, keine Autoklasse zu erstellen, wenn ich keine definierte Rolle dafür habe?

Ich meine, ist es in Ordnung, ein Lenkrad zum Fahren, BoltsAndNuts auf Rädern für die Boxencrew und alle möglichen anderen lustigen Schnittstellen zu haben, ohne eine Instanz zu haben, die ein Auto als Ganzes darstellt?

Anton Petrov
quelle

Antworten:

24

Was nützen ein paar Autoteile, die auf einer Rennstrecke sitzen, jemandem?

Wenn Ihre Autoklasse nur Autoteile hält, ist dies genauso nützlich wie ein nasser Beutel mit Teilen.

Verwendungszweck

Was ich als Fahrer will, kann ich kontrollieren. Das reagiert, wenn ich nach Geschwindigkeit frage. Das funktioniert wie auf Schienen. Das kann auf einen Cent aufhören.

Was ich will, ist eine Autoklasse, die nutzbar ist. Dass ich sagen kann, Dinge zu tun, ohne darüber nachdenken zu müssen, wie der Vergaser funktioniert. Ich denke nur an das Gaspedal. Wie diese miteinander verbunden sind, ist nichts, worüber ich mir Sorgen mache. Das ist Abstraktion.

Abhängigkeitsspritze

Die Abhängigkeitsinjektion hat damit nichts zu tun. Wenn ich das Auto fahre, denke ich nicht daran, wie es gebaut wurde. Solange es funktioniert, ist es mir egal, wie sie es zusammensetzen.

Nein, DI ermöglicht es meiner Boxencrew, meine Reifen schnell gegen bessere auszutauschen, wenn es auf der Strecke zu regnen beginnt. Es ist schön, das zu können, ohne in ein ganz anderes Auto steigen zu müssen.

Bei DI geht es wirklich darum, einem Prinzip zu folgen: Trennung von Konstruktion.

Ein Auto, das neue Reifen mit einer Geschwindigkeit von 90 Meilen pro Stunde einbauen kann, klingt vielleicht cool, aber ich glaube nicht, dass es Rennen mit einem Reifen gewinnen wird, der einen Apparat installiert.

Bei DI geht es darum, Ihre Teile so zu installieren, dass Ihre Boxencrew sie erreichen kann. Wenn Sie newan derselben Stelle verwenden, an der Sie das Verhalten programmieren, ist es, als würden Sie den Vergaser an Ort und Stelle schweißen. Sicher, ein Acetylenbrenner könnte es entfernen, aber bitte ziehen Sie in Betracht, zuerst Schrauben und Muttern zu verwenden.

Das ist DI. Sicher ist es nicht so einfach new, etwas zu erfinden, sobald Sie erkennen, dass Sie es wollen. Stattdessen müssen Sie separaten Code schreiben, der weiß, wie Sie Ihr Auto bauen. Aber es macht es sicher einfacher, Dinge später zu ändern. Und das bedeutet, dass Sie kein Auto-Montagewerk mit sich herumschleppen müssen.

Konstruktion

Irgendwo muss etwas wissen, ob die Reifen Goodyear sind. Wo soll dann die Auto-Bauordnung stehen? Wenn nicht das Auto, dann die Boxencrew? Nein, die Strecke? Alle haben Verhaltenscode. Code, der während des Rennens ausgeführt werden muss. Der Bau des Autos sollte vor dem Rennen an einem Ort erfolgen, der nicht im Verhaltenscode enthalten ist. Mark Seemans nannte diesen Ort die Kompositionswurzel . Die meisten Leute nennen es main.

Es ist ein einfaches Muster. Erstellen Sie im Wesentlichen das Objektdiagramm und rufen Sie dann eine Verhaltensmethode für ein Objekt im Objektdiagramm auf. Das ist es. Dies ist der EINZIGE Ort, an dem Konstruktion und Verhalten zusammen sein müssen.

Das bedeutet nicht, dass die Konstruktion ein Stapel von Verfahrenscodes sein muss, die alle nacheinander in der Hauptsache angeordnet sind. Sie können jedes Werkzeug in der Sprache verwenden, um Konstruktionen durchzuführen. Vermische es einfach nicht mit Verhalten.

Dies in der Sprache zu tun und kein DI-Framework oder IoC-Container zu verwenden, wird als reines DI bezeichnet . Es funktioniert sehr gut. Hat schon lange. Früher nannten wir es einfach Referenzübergabe .

DI-Werkzeuge

Was Ihnen ein DI-Tool kauft, sind die Konstruktionsdetails, die in eine andere Sprache (xml, json, was auch immer) verschoben wurden, um die Trennung zwischen Konstruktion und Verhalten zu erzwingen. Wenn Sie Ihren Programmierkollegen nicht vertrauen, newdass sie sie nicht verwenden, wenn sie es nicht sollten, kann dies ansprechend sein.

Der Nachteil ist, dass es verlockend ist, die Details des DI-Tools in der gesamten Codebasis zu verteilen. Manchmal wird die Codebasis mit proprietären Anmerkungen infiziert. Die Beispiele, die sie liefern, unterstützen dies sicherlich. Das Tool tendiert dazu, in den Sprachraum zu wechseln, bis Sie den Job nicht nur als Java-Programmierjob, sondern als Java / Spring-Programmierjob bewerben können.

Design-Prinzipien

Ich habe lange Zeit Autoklassen programmiert, weil mir klar war, dass ich eine Autoklasse brauche, wenn ich eine Autologik programmiere. Aber mit DI ist es mir nicht so offensichtlich. Sie fragen sich immer noch, ob es für DI eine Redewendung ist, keine Autoklasse zu erstellen, wenn ich keine definierte Rolle dafür habe?

Ich denke, Sie lernen etwas über Abstraktion und ändern, wie Sie entscheiden, dass eine Klasse benötigt wird. Das ist gut. Aber hier geht es nicht um DI. DI hilft Ihnen nicht bei der Entscheidung, ob Sie eine Autoklasse benötigen. DI hilft Ihnen, das Auto davon abzuhalten, zu wissen und sich darum zu kümmern, ob es sich bei den Reifen um Goodyear-Reifen handelt. DI hilft Ihnen dabei, nicht zu wissen, ob die Autos in Japan hergestellt werden.

Eine der grundlegendsten Fragen im Software-Design lautet: "Was weiß über was?" Das ist die Hauptsache, die Ihnen ein UML-Diagramm zeigt. Wenn Sie etwas neu erfinden, erreichen Sie die Schnittstelle zu dem konkreten Element, an das Sie jetzt gebunden sind. Das Auto muss jetzt wissen, dass die Reifen Goodyear sind. Was irgendwie scheiße ist, wenn Michelin Sie sponsern möchte.

Um dies zu vermeiden, wird nach dem Prinzip der Abhängigkeitsinversion aufgerufen . Formal sollte ein High-Level-Modul (wie die Fahrzeugklasse) nicht direkt von Low-Level-Modulen (wie die GoodyearTire-Klasse) abhängen. Es sollte von einer Abstraktion abhängen (wie eine Reifenschnittstelle).

Eine Möglichkeit, dies zu vermeiden, wird als Umkehrung der Kontrolle bezeichnet . Hier liegt der Schwerpunkt auf der Änderung des Kontrollflusses. Bewegen die Reifen das Auto oder bewegt das Auto die Reifen? Wenn wir richtig darüber nachdenken, können wir das Auto und die Reifen nicht statisch miteinander verbinden. DI ist eine besondere Möglichkeit, der Inversion der Kontrolle zu folgen.

Nichts davon sagt Ihnen, ob Sie eine Autoklasse benötigen. Wenn Sie "Autologik" programmieren, ist es schön, wenn es einen Ort gibt, an dem Sie sie aufbewahren können, anstatt sie überall zu verteilen. Lassen Sie sich einfach nicht davon täuschen, dass die Logik des Fahrzeugbaus mit der Logik des Fahrzeugverhaltens identisch ist, sodass alles am selben Ort leben muss. Aber wenn Sie keine definierte Rolle für ein Auto haben, brauchen Sie auch keine. Fahren Sie Motorräder um die Strecke, wenn Sie möchten.

Ich meine, ist es in Ordnung, ein Lenkrad zum Fahren, BoltsAndNuts auf Rädern für die Boxencrew und alle möglichen anderen lustigen Schnittstellen zu haben, ohne eine Instanz zu haben, die ein Auto als Ganzes darstellt?

DI oder kein DI, es ist in Ordnung, eine Instanz zu haben, die ein Auto als Ganzes darstellt, aber diese Instanz möchte ich nicht direkt wissen, wenn ich nicht muss. Geben Sie mir eine Autoabstraktion, damit es mir egal ist, ob es mit Gas, Diesel oder Strom betrieben wird, wenn ich es benutze. Das ist nur etwas, das mich interessieren sollte, wenn ich es baue oder pflege. Es ist schön, wenn der Code, der das Auto verwendet, nicht wissen oder sich darum kümmern muss, wie es funktioniert. "Ich weiß es nicht. Ich will es nicht wissen."

candied_orange
quelle
Es wäre cool, wenn Sie nicht die Metapher der Auto- und Boxencrew für eine Frage verwenden würden, die sie zur Darstellung von Code verwendet. Trotzdem eine gute Antwort.
S. Tarık Çetin
6
Und ich dachte immer, Inversion of Control sei, wenn man nach links
lenkt,
CandiedOrange, also keine Autoklasse, wenn es keine sinnvolle Schnittstelle und klar definierte Verantwortung gibt? Und kein Car.h in meinem Projekt. Und Kollege, der in meinen Code schaut und sich fragt, ob es sich um einen E-Shop für Autoteile oder eine Anwendung zur Verwaltung von Werkstätten handelt? :)
Anton Petrov
@AntonPetrov "Wenn es wie eine Ente aussieht und wie eine Ente quakt, aber Batterien benötigt, haben Sie wahrscheinlich die falsche Abstraktion". Gute Namen sind sehr wichtig und schwer vorstellbar, sollten jedoch geändert werden, um die klar definierte Verantwortung und die sinnvolle Benutzeroberfläche widerzuspiegeln, damit sie nicht überraschen. Wenn ich den Namen lese, dann schaue auf die Benutzeroberfläche und denke "das ist ziemlich genau das, was ich erwartet habe", dann hast du einen guten Namen.
candied_orange
15

Es sieht so aus, als sollte ich keine Autoklasse definieren, nur um Autoteile zu halten, da alle Autoteile voneinander abhängen und diese Suppe von Teilen ein Auto ist. Habe ich recht?

Nein.

Der Punkt der Abhängigkeitsinjektion besteht überhaupt nicht darin , Abhängigkeiten zu entmutigen. Ganz im Gegenteil.

Dependency Injection geht es um die Frage: woher kommt das Auto seine Teile erhalten aus ? Wo und wie entstehen sie und wie erhält das Auto Hinweise auf sie?

Naiv würde das Auto seine Teile selbst herstellen, indem es anruft new. Dies ist einfach und unkompliziert, aber sehr unflexibel. Das Auto muss alles wissen, was zum Erstellen der Teile erforderlich ist, und wenn Sie jemals zwei Autos mit unterschiedlichen Teilen haben möchten, muss diese Logik auch in das Auto einfließen.

Bei der Abhängigkeitsinjektion wird die gesamte Logik aus dem Fahrzeug entfernt und in eine Konfigurationskomponente eingefügt, die alle Teile erstellt und die Referenzen verdrahtet. Die vollständig konfigurierten Abhängigkeiten werden in das Auto injiziert - und untereinander.

Michael Borgwardt
quelle
1
Zum Schluss eine vernünftige Erklärung des dependency injectionKonzepts.
Anatoly Techtonik
1
Ich würde sagen, das üblichere System für den "naiven" Ansatz ist nicht, dass das Auto als neu bezeichnet wird, sondern dass der Benutzer der Fahrzeugklasse seine Teile seinen Eigenschaften zuordnet und in der Zwischenzeit die Fahrzeugklasse in ist ein unbestimmter Zustand. DI bietet ein Mittel zur mechanischen Sicherstellung der Klassengültigkeit.
Whatsisname
2
@whatsisname Ich kann DI leicht verwenden, um ungültige und halb initialisierte Objekte zu erstellen. Die Validierung liegt nicht in der Verantwortung von DI. Es ist auch nicht unveränderlich. DI kann die Aufgabe übernehmen, gültige und unveränderliche Objekte zu erstellen. DI kann nicht erzwingen, dass dies die einzigen Möglichkeiten sind, wie diese Objekte konstruiert und verwendet werden. Die Objekte müssen dies erzwingen.
candied_orange
@CandiedOrange: Du kannst also einfach jede Technik verwenden, um etwas halbherziges zu machen, na und? Das Erfordernis von Abhängigkeiten beim Konstruktor und deren Unveränderlichkeit ist kein Wundermittel, aber dennoch eine sehr nützliche Methode, um viele häufig auftretende schlechte Situationen zu verhindern.
Whatsisname
0

Wenn ich also alle meine Rennwagen in die RaceTrack stecke, gibt es keine Autoeinheiten, sondern viele Autoteile, die voneinander abhängig sind und auf der Strecke fahren? Habe ich recht?

Gut. Ja und nein. Eine Autoentität ist noch gültig zu haben. Und ein Auto ist mehr als die Summe seiner Teile. Sie brauchen auch einen Fahrer (vorerst). Oft haben Rennwagen auch Namen, ganz zu schweigen von Marke und Modell des Autos.

Ja, Autos sind im Grunde genommen ein Container mit Teilen, aber es ist diese Verpackung und Zusammenarbeit von Teilen, die etwas Größeres ausmachen: ein Auto.

Also wirklich nein. Ein Auto ist nicht nur eine zufällige Tüte aus Metall und Plastik. Es ist eine Maschine.

Die Abhängigkeitsinjektion ist in diesem Fall nicht übertrieben. Etwas anderes muss das Auto bauen, das eine Fabrikklasse oder ein Builder-Objekt wäre. Das Auto sollte nicht wissen, wie man sich selbst baut.

Greg Burghardt
quelle
2
Sie können DI ohne Fabriken oder Builder-Objekte haben. Einfache Konstruktorparameter sind eine gültige Methode, die unter vielen Umständen funktioniert.
Whatsisname