Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Julién

Alter Hase

  • »Julién« ist der Autor dieses Themas

Beiträge: 717

Wohnort: Bayreuth

Beruf: Student | Hilfswissenschaftler in der Robotik

  • Private Nachricht senden

1

11.04.2015, 23:49

Dependency Injection | Was ist das und welches Problems löst es?

Hi,
letztens hat mich BlueCobolds Antwort auf einen Thread auf das Thema "Dependecy Injection" gebracht.

Kurz:
In dem Post geht es darum, ob ein Logger für ein Spiel, denn ein Singleton oder eine globale Instanz seien sollte.
Vorgeschlagene Lösung: DI

Ich habe von "DI" schon vorher etwas gehört und gelesen, aber es scheint so als würde ich es einfach nicht verstehen.
Deshalb die Frage(n):
Was ist DI?
Welches Problem löst es?
Wie könnte eine Implementation in C++ aussehen?


Mit freundlichen Grüßen,
Julien
I write my own game engines because if I'm going to live in buggy crappy filth, I want it to me my own - Ron Gilbert

Tobiking

1x Rätselkönig

  • Private Nachricht senden

2

12.04.2015, 00:42


Was ist DI?

DI hängt mit den Begriffen Inversion of Control (IoC) und Single responsibility principle (SRP) zusammen. Man möchte das eine Klasse sich nur um eine klar definierte Aufgabe kümmert (SRP). Dazu gehört nicht die Auflösung von nötigen Abhängigkeiten. Nötige Abhängigkeiten sollen außerhalb der Klasse aufgelöst werden und dann der Klasse übergeben/injeziert werden.


Welches Problem löst es?

Es ermöglicht vollständig unabhängige Klassen. Löst eine Klasse ihre Abhängigkeiten selber auf, muss sie wissen wie das geht. Sie muss auf irgendein Singleton, eine globale Variable oder sonstiges Konstrukt zurückgreifen und hat damit eine starke Kopplung daran. Ist dieses Konstrukt nicht vorhanden, kann die Klasse nicht funktionieren. Sowas passiert wenn man z.B. Unittests schreibt oder mal eine Klasse in ein neues Projekt übernehmen möchte oder einfach nur mal alten Code umbaut und dann merkt das es wegen solchen Abhängigkeiten nicht geht ohne überall etwas zu ändern.


Wie könnte eine Implementation in C++ aussehen?

DI wird zwar meist mit Frameworks in Verbindung erwähnt, das eigentliche Prinzip dahinter ist aber gar nicht so komplex. Eine Klasse, die im Konstruktor per Hand die Abhängigkeiten übergeben bekommt, zählt praktisch schon zu DI. Alternativ kann die Klasse auch Set-Funktionen für die Abhängigkeiten haben.

Der nächste Schritt wäre wohl statt immer von Hand die Objekte mit allen Abhängigkeiten zu füllen diese über eine Factory erzeugen zu lassen. Die eigentlichen Parameter gibt man der Factory mit, die Abhängigkeiten werden von der Factory bei der Erzeugung übergeben.

Von da an wird es komplexer und geht immer weiter in richtung Framework, wo ich nicht weiß wie der Stand bei C++ aussieht. Ich bin aber auf ein Proposal von Boost gestoßen https://github.com/krzysztof-jusiak/di/tree/cpp14 das auch allgemein zu dem Thema DI noch einige Infos und Quellen hat.

3

12.04.2015, 20:41

Ein wichtiges Stichwort ist "Injection", meistens verwendet für Constructor Injection.
In meinem Fall war es dafür gedacht (das mit dem Logger):

Meine Klasse X benötigt einen Logger. Meine Klasse Z benötigt keinen Logger, aber die Klasse X.
Überall wo ich jetzt meine Klasse instanziiere, müsste ich im Constructor den Logger mitgeben, damit ich nur eine Instanz meines Loggers habe.
Also: "new X(logger);", überall wo X benötigt wird.
Da es aber nicht Aufgabe meiner Klasse Z ist, den Logger zu kennen, beziehungsweise die Abhängigkeiten von X zu kennen (oder X instanziieren), soll ein IoC Container das ganze Lösen.

damit könnte ich sage:

container.create<X>();
container.create<Z>();

und der IoC Container fügt alle Abhängigkeiten ein.

Da C++ aber nicht über Reflection verfügt, müssen hier entweder die Abhängigkeiten einmal eingegeben werden, oder ein Reflection-System programmiert werden.

Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

4

12.04.2015, 20:53

Tobiking hat ja bereits geschrieben, dass DI eigentlich nur beschreibt, dass Abhängigkeiten von außen zugewiesen werden. Da das bereits über Konstruktorparameter, Set-Methoden, Properties und Zuweisungen an Membervariablen möglich ist, braucht man dafür kein Reflection. Das heißt, dass DI auch in C++ möglich ist.
Um auf deine Situation einzugehen: X benötigt den Logger und für Z wäre der Logger überflüssiger Ballast: vielleicht sollte sich sich Z gar nicht um die Instanziierung kümmern und das X-Objekt als Parameter erhalten?
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

Tobiking

1x Rätselkönig

  • Private Nachricht senden

5

13.04.2015, 08:51


Da es aber nicht Aufgabe meiner Klasse Z ist, den Logger zu kennen, beziehungsweise die Abhängigkeiten von X zu kennen (oder X instanziieren), soll ein IoC Container das ganze Lösen.

damit könnte ich sage:

container.create<X>();
container.create<Z>();

und der IoC Container fügt alle Abhängigkeiten ein.

Da C++ aber nicht über Reflection verfügt, müssen hier entweder die Abhängigkeiten einmal eingegeben werden, oder ein Reflection-System programmiert werden.

IoC Container werden zwar recht oft verwendet, gelten aber als Anti-Pattern. Das einzige Problem das der IoC Container löst ist die Testbarkeit, da man nun im Unittest einfach die Abhängigkeiten ersetzen kann. Der Knackpunkt ist wie X an den Container kommt. Ist der Container global oder ein Singleton, haben wir wieder eine fixe Abhängigkeit. Wird er immer mit durchgereicht, haben wir wieder die Situation das wir Objekte durchreichen, die irgendwo vielleicht mal gebraucht werden könnten.

Wie Scaldur geschrieben hat, wäre eine Lösung für das beschriebene Problem, dass Z bereits ein fertiges X bekommt. Wenn Z wirklich eine Möglichkeit braucht X-Instanzen zu erzeugen, kann man Z eine Factory dafür übergeben. Die Factory scheint zwar dem IoC Container, der im Prinzip auch nur dafür sorgt das eine gebrauchte Abhängigkeit erzeugt/zurückgegeben wird, ähnlich, aber ist nicht so global und allwissend. Wenn das mit den Factories Überhand nehmen sollte, müsste man mal überlegen warum man überhaupt so eine tiefe Hierarchie für die Erzeugung von Instanzen hat und warum die Instanzen plötzlich so viele Abhängigkeiten haben, die vorher nicht gebraucht wurden.

Toa

Alter Hase

Beiträge: 944

Beruf: Research associate

  • Private Nachricht senden

6

13.04.2015, 14:29

...
IoC Container werden zwar recht oft verwendet, gelten aber als Anti-Pattern. Das einzige Problem das der IoC Container löst ist die Testbarkeit, da man nun im Unittest einfach die Abhängigkeiten ersetzen kann. Der Knackpunkt ist wie X an den Container kommt. Ist der Container global oder ein Singleton, haben wir wieder eine fixe Abhängigkeit. Wird er immer mit durchgereicht, haben wir wieder die Situation das wir Objekte durchreichen, die irgendwo vielleicht mal gebraucht werden könnten.
+1 für diese Antwort. Ich benutze Dependency Injection auch super gerne, weil ich in meinen Tests einfach alles wegmocken kann. Aber wie auch von Tobiking erwähnt, ist der Einsatz umstritten und endet oft in grunlegenden Religionsfragen.

€dit: Als Beispiel lässt sich das "Play Framework" anführen, welches in der neuen Version 2.4 Dependency Injection einführt, um seinen globalen Zustand los zu werden.
"Das ist ein Minkovski Raum, manche Menschen nennen ihn auch Weltraum" Prof. Dr. Jürgen Wambach, Theoretische Physik, TU Darmstadt | Meine Homepage

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »Toa« (13.04.2015, 14:37)


Werbeanzeige