IOC Container Design

Design Patterns, Erklärungen zu Algorithmen, Optimierung, Softwarearchitektur
Forumsregeln
Wenn das Problem mit einer Programmiersprache direkt zusammenhängt, bitte HIER posten.
Antworten
snoob741
Beiträge: 76
Registriert: 03.04.2012, 22:36

IOC Container Design

Beitrag von snoob741 »

Ich bin aktuell dabei mich einen leichtgewichtigen, c++11 template basierten IOC Container aufzubauen. Dieser soll auch ein lifetime Management unterstützen, so dass ich endlich von meinen Singletons runter komme (ist schon mehr als überfällig). Soweit so gut und ich bin auch schon in der Lage einfache Klassen, Instanzen zu registrieren und abzufragen, wobei ich unterscheiden kann ob die Instanzen neu erzeugt oder als singleton zurückgegeben werden. Die abfrage erfolgt üblicherweise über den Typen:

Code: Alles auswählen

        container.registerInstance<IDataChunk>(std::make_shared<DataChunk>());
        auto chunk = container.resolve<IDataChunk>();
Nun mache ich mir grade Gedanken über Interfaces, von denen es unterschiedliche Ausprägungen gibt. Mir fällt da sofort die Textur ein, von der ich ein Interface, aber 3 Ausprägungen habe. Ein anderes Beispiel währe ein Logger oder die Shader von denen es mehrere Ausprägungen geben kann bzw. gibt. Bisher habe ich die den Typen für die konkrete Ausprägung per Enumeration als primitiven Konstruktor Parameter eingesteuert und die konkrete Instanz über eine Factory erzeugt, so dass ich automatisch den konkreten Typ erhalten habe. Doch wie mache ich das bei einem IOC Container, der ja sozusagen die Factory Funktion übernimmt und nach der Registrierung die konkrete Klasse über den Typen liefert. Schwierig, wenn ich gegen dass Interface arbeiten will, ohne dass sich der Nutzer Gedanken über die konkreten Klassentypen machen soll, da er bzw. einzelne Module nicht weiss welche konkrete Render API konkret verwendet wird.

Ich habe gesehen, einige IOC Container bieten die Möglichkeit, Komponenten mit einer ID zu registrieren und die konkrete Instanz über diese ID abzufragen. Ist machbar finde ich aber nicht so schön, da dies schon fast Konstanten-Listen impliziert um übergreifend immer die selben ID's abzufragen. Ich könnte für diese Fälle auch direkt die Factory registrieren und die Objekte über diese dann wie gehabt erzeugen. Ich hab auch gelesen, dass einige IOC Container die Übergabe von Parametern als Listen ermöglichen.

Mich würde interessieren, was Ihr für einen Ansatz bevorzugen würdet. Oder habt Ihr gar noch eine bessere Idee?
Benutzeravatar
Aramis
Moderator
Beiträge: 1458
Registriert: 25.02.2009, 19:50
Echter Name: Alexander Gessler
Wohnort: 2016
Kontaktdaten:

Re: IOC Container Design

Beitrag von Aramis »

Oder habt Ihr gar noch eine bessere Idee?
IOC mag generell ein nettes Pattern sein, wenn ich hier aber herauslese, dass du Texturen per IOC bereitstellen willst, frage ich mich, ob das an dieser Stelle nicht totales Overengineering ist.
Doch wie mache ich das bei einem IOC Container, der ja sozusagen die Factory Funktion übernimmt und nach der Registrierung die konkrete Klasse über den Typen liefert. Schwierig, wenn ich gegen dass Interface arbeiten will, ohne dass sich der Nutzer Gedanken über die konkreten Klassentypen machen soll, da er bzw. einzelne Module nicht weiss welche konkrete Render API konkret verwendet wird.
Das verstehe ich nicht, kann der IOC-Container nicht gerade dieses Wissen (z.b. welche Render-API dahintersteckt) verwenden, um die passende Instanz bereitzustellen?

Persoenlich bin ich bei Spielprojekten fuer handgeschriebene DI a.k.a Singletons als lokale Variablen erstellen und dann in alles reinreichen, was sie braucht.
snoob741
Beiträge: 76
Registriert: 03.04.2012, 22:36

Re: IOC Container Design

Beitrag von snoob741 »

IOC mag generell ein nettes Pattern sein, wenn ich hier aber herauslese, dass du Texturen per IOC bereitstellen willst, frage ich mich, ob das an dieser Stelle nicht totales Overengineering ist.
Texturen waren nur ein Beispiel, was mir sofort zu dem Thema in den Kopf gekommen ist. Laden werde ich sie später über den AssetManager, aber es wär schon praktisch, wenn ich den IOC Container einheitlich für die Instanziierung verwenden könnte.
Das verstehe ich nicht, kann der IOC-Container nicht gerade dieses Wissen (z.b. welche Render-API dahintersteckt) verwenden, um die passende Instanz bereitzustellen?
Ja und genau hier habe ich zur Zeit das gedankliche GAP. Ich greif noch mal die Texturen auf, um das Beispiel zu erläutern. Ich habe ein Interface ITexture und 2 konkrete Implementationen im Fall des OpenGL Renderers, GLTexture2D und GLTexture3D. Registrieren würde ich die wie folgt beim Hochfahren des Render Plug-Ins:

Code: Alles auswählen

container.bind<ITexture, GLTexture2D>();
container.bind<ITexture, GLTexture3D>();
Mal abgesehen davon, dass ich noch keine konkrete Idee habe wie ich die halte, evtl. als map<std::type_index, std::vector<std::type_index>> (es soll in jedem Fall für ein Interface oder eine konkrete Klasse nur einmal die Registrierung möglich sein). In diesem Fall habe ein Interface und 2 konkrete Implementationen. Abfragen würde man sie normalerweise über das Interface, da der Aufrufer ja nicht die konkrete Renderimplementation kennen muss:

Code: Alles auswählen

container.resolve<ITexture>();
Woher soll der Container wissen, was er erzeugen soll, wenn ich hier den Resolve-Aufruf nicht parametrisiere. Eine Idee wäre eine namens ID, was aber voraussetzt dass jeder Aufrufer die ID kennt. Denke diesen Ansatz muss ich so oder so umsetzen, ist aber für eine Typen abhängige Erzeugung meiner Meinung nach ungeeignet und eher für Instanzen verwendet, die als Singleton gehalten werden. Alternativ wären Parameter die man hier mitgeben kann, ich meine aber mehrfach gelesen zu haben, dass dies eigentlich das IOC Prizip durchbricht und man den Container zum Service-Pattern umfunktioniert. Ein weiterer Ansatz wäre es die Factory zu registrieren und über sie die Texturen zu erzeugen.

Alle Ansätze haben irgendwie Ihre Pros und Contras. Bin mir absolut unschlüssig was ideal wär. Daher die Frage, wie Ihr das umsetzen würdet, bzw. habt, wenn Ihr das IOC-Pattern nutzt ...
snoob741
Beiträge: 76
Registriert: 03.04.2012, 22:36

Re: IOC Container Design

Beitrag von snoob741 »

Bin gestern noch einen Schritt weiter gekommen. Ich benutze nun für die Datenhaltung eine std::unordered_multipmap, was mir zumindest jetzt schon einmal ganz Einfach die Abfrage des konkreten Typens bzw. Instanz über den konkreten Typen ermöglicht:

Code: Alles auswählen

        container.bind<TestA, ITest>();
	container.bind<TestB, ITest>();
	
	auto testA = container.resolve<TestA>();
	std::cout << "Resolved: " << testA->getName() << std::endl;
	
	auto testB = container.resolve<TestB>();
	std::cout << "Resolved: " << testB->getName() << std::endl;
Da hat mir die mir Multimap ne Menge Arbeit abgenommen. Soweit so gut, nur Löst es das Problem nicht, wenn ich nicht den konkreten Typ kenne. Langsam denke ich, ich mach mir hier zuviel Gedanken und ich sollte dort dann tatsächlich auf eine Factory zurückgreifen ...
Benutzeravatar
Aramis
Moderator
Beiträge: 1458
Registriert: 25.02.2009, 19:50
Echter Name: Alexander Gessler
Wohnort: 2016
Kontaktdaten:

Re: IOC Container Design

Beitrag von Aramis »

Ich verstehe noch immer nicht, wieso ein IOC-Container hier kein Overengineering ist :-)
Benutzeravatar
Schrompf
Moderator
Beiträge: 4838
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: IOC Container Design

Beitrag von Schrompf »

Meine Idee dazu lautet: lass den Quatsch. Warum man Texturen, Logger und Shader zusammen global ablegen können soll, musst Du mir erstmal erklären.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
snoob741
Beiträge: 76
Registriert: 03.04.2012, 22:36

Re: IOC Container Design

Beitrag von snoob741 »

Mag sein das es etwas Overengineered ist, mir gefällt jedoch das Konzept zentral von einem Ort aus die Objekterzeugung bzw. Verwaltung zu steuern.

Schrompf, es geht in erster Linie darum transiente Objekte zentral zu erzeugen singleton Verwaltung wie z.b. Logger ist eher ein Zusatz. Wobei ich echte Singleton Implementationen vermeiden kann.
NytroX
Establishment
Beiträge: 358
Registriert: 03.10.2003, 12:47

Re: IOC Container Design

Beitrag von NytroX »

Hi, also ich denke auch du solltest nochmal genau überlegen, welches Problem du lösen willst und ob das Pattern da passt.

Und ob du dafür echt C++11 brauchst, oder das nicht einfacher geht.
Eine einfache Lösung wäre Constructor bzw. Method Dependency Injection, d.h. du gibst die "echte" Klasse beim bauen an der zentralen Stelle an, Beispiel findest du hier:
http://www.codeproject.com/Articles/615 ... ency-Inver

Und hier das volle Teil mit Templates usw (etwa in der Mitte), sieht stark nach dem aus, was du versuchst :D
http://www.codeproject.com/Articles/567 ... dicplusTem
(PS: die noop() im IHolder sollte wohl eigentlich ein virtueller Destruktor sein... was tut der da? :shock: )


Wie auch immer, ich denke mit einer Factory wärst du einfacher dabei.
Wie bereits von den anderen erwähnt würde mir auch kein Szenario einfallen, wo man das alles in EINEM container braucht oder haben will.
Ich verstehe noch nicht, warum da einfache Interfaces nicht ausreichen.
Abgesehen davon macht das ganze auch viel mehr Sinn, wenn man in verschiedenen Modulen/dlls arbeitet und neue Funktionalität entwickeln kann, ohne das alte Programm zu modifizieren.
Aber das geht ja mit Templates so hervorragend in C++... :P ... In C#/Java macht das Pattern schon mehr Sinn, wenn man Reflection für Binaries hat (müsste man in C++ halt drumrum arbeiten).
snoob741
Beiträge: 76
Registriert: 03.04.2012, 22:36

Re: IOC Container Design

Beitrag von snoob741 »

Danke für die Artikel, an denen orientiere ich mich unter anderem. Das DJ-Pattern nutze ich bereits seit Jahren, sowohl für Constructor als auch Method injection. Für den IOC Container habe ich mich enschieden, da ich mehrfach irgendwelche Manager-Singletons habe, die irgend etwas verwalten, bzw. notwendig sind um zur Laufzeit API spezifische Abhängigkeiten verfügbar zu machen, indem u.A. Factories beim Initialisieren der Plug-Ins zuvor registriert worden sind z.B. für Shader, Imaging.

Und genauf davon wollte ich weg kommen. Ich muss jedes mal überlegen, welchen Manager habe ich wofür, wie komme ich da ran? Durch den IOC Container, der übrigens nun in der Basis funktional ist (muss hier und da noch nen bissl feilen) habe ich nur noch ein Singleton, die Applikation selbst. Über diese registriere ich beim Hochfahren alle relevante Ojekte und kann dann bei Bedarf zentral über die Applikation den Container abrufen und neue transiente Objekte instanziieren bzw. als singleton abfragen. Im übrigen kann sich der Anwender dafür entscheiden den Container zu nutzen, muss es nicht, er kann auch ganz gewöhnlich Ojbekte selbst erzeugen nur intern werde ich ihn mit sicherheit verwenden.
NytroX
Establishment
Beiträge: 358
Registriert: 03.10.2003, 12:47

Re: IOC Container Design

Beitrag von NytroX »

Ah, ich habe auf Gamedev noch einen Artikel gefunden, der vielleicht noch besser passt:
http://www.gamedev.net/page/resources/_ ... time-r3736

Hat mit IOC nicht viel am Hut, aber es geht um dein eigentliches Problem, die Verwaltung der "Singleton"-Objekte (sind ja keine echten Singletons mehr).
Antworten