Datenbankzugriffsschicht ohne mit Singletons um sich zu schmeißen?
-
Ok und das nennt sich schon Dependency Injection? Ich hatte mir etwas kniffligeres vorgestellt, aber gut..
-
@dot: Findest du constructor-injection gut weil du es hier dauernd praktizierst?
Und vielleicht sollten wir für C++ nicht solcherlei Java-Beispiele bringen, da das dort ohnehin nicht state-of-the-art ist wie volkard uns bereits mitgeteilt hat.
MfG SideWinder
-
SideWinder schrieb:
@dot: Findest du constructor-injection gut weil du es hier dauernd praktizierst?
Und vielleicht sollten wir für C++ nicht solcherlei Java-Beispiele bringen, da das dort ohnehin nicht state-of-the-art ist wie volkard uns bereits mitgeteilt hat.
MfG SideWinder
Okay, wie sieht denn dann das typische state-of-the-art c++ Beispiel aus?
-
Dependency Injection im Constructor bringt zwar ein paar Vorteile gegenüber einem Singleton, aber braucht auch mehr Speicher und Performance, wenn man z.B. 1000000 Objekte erstellt und denen allen einen Logger zuweist.
-
SideWinder schrieb:
@dot: Findest du constructor-injection gut weil du es hier dauernd praktizierst?
Was gäbe es denn für Alternativen in C++?
bewertung schrieb:
Dependency Injection im Constructor bringt zwar ein paar Vorteile gegenüber einem Singleton, aber braucht auch mehr Speicher und Performance, wenn man z.B. 1000000 Objekte erstellt und denen allen einen Logger zuweist.
Der Zugriff auf einen Singleton über die getInstance() Methode ist auch kein Leichtgewicht. Und wenn du 100000 Objekte hast die alle auf einen anderen Logger loggen können müssen hast du sowieso ganz andere Probleme...
-
dot schrieb:
SideWinder schrieb:
@dot: Findest du constructor-injection gut weil du es hier dauernd praktizierst?
Was gäbe es denn für Alternativen in C++?
* Setter
* Thread-Locals
-
hustbaer schrieb:
* Setter
Damit ändert sich doch am Prinzip nix!?
hustbaer schrieb:
* Thread-Locals
Das hätte doch eine vollkommen andere Semantik. Das hilft mir doch nur wenn ich die Dependency eben an einen bestimmten Thread binden will und nicht an ein bestimmtes Objekt!? Ich würde das jetzt nicht als Alternative sehen sondern als was völlig Orthogonales.
Aber die Idee das Logging nicht auf einer per-Komponenten- sondern einer per-Thread-Basis zu machen ist sehr interessant und unter Umständen wirklich sehr elegant
, werd ich mir auf jeden Fall merken
-
dot schrieb:
hustbaer schrieb:
* Thread-Locals
Das hätte doch eine vollkommen andere Semantik. Das hilft mir doch nur wenn ich die Dependency eben an einen bestimmten Thread binden will und nicht an ein bestimmtes Objekt!? Ich würde das jetzt nicht als Alternative sehen sondern als was völlig Orthogonales.
Naja...
Sagen wir mal so...
Thread-Locals sind ein möglicher Weg, kontextbezogene Lookups zu machen.
Und kontextbezogene Lookups kann man wiederum verwenden um Inversion of Control zu machen.
Und Inversion of Control ist schon eng verwandt mit Dependency Injection.Nochmal dazu:
Das hilft mir doch nur wenn ich die Dependency eben an einen bestimmten Thread binden will und nicht an ein bestimmtes Objekt!?
Es reicht ja oft (meistens?), dass man Dependencies an einen "Task" ("Request", "Job") binden kann.
Wenn das Programm so strukturiert ist, dass es, während der Lebenszeit eines Tasks, eine 1:1 Beziehung Task:Thread gibt, dann reicht es, z.B. einen Service Locator an einen Thread zu binden.
Wenn es so eine 1:1 Beziehung nicht gibt wird es etwas komplizierter. Lässt sich aber auch noch machen, man muss dann halt an allen Stellen wo ein Thread anfängt/aufhört für einen Task tätig zu werden die Thread-Local Variable entsprechend anpassen. (Kann man z.B. ohne grossen Aufwand machen, indem man alle Callbacks/Thread-Funktionen in einen Hilf-Funktor einwickelt.)
Alternativ kann man natürlich den Service Locator bzw. den Task überall als Parameter rumreichen.
-
dot schrieb:
bewertung schrieb:
Dependency Injection im Constructor bringt zwar ein paar Vorteile gegenüber einem Singleton, aber braucht auch mehr Speicher und Performance, wenn man z.B. 1000000 Objekte erstellt und denen allen einen Logger zuweist.
Der Zugriff auf einen Singleton über die getInstance() Methode ist auch kein Leichtgewicht.
Eigentlich schon, wenn man eine createInstance Methode einführt. Dann fallen auch ein paar andere Probleme wie Initialisierungsreihenfolge weg.
dot schrieb:
Und wenn du 100000 Objekte hast die alle auf einen anderen Logger loggen können müssen hast du sowieso ganz andere Probleme...
Wieso auf einen anderen, du hast auch beim gleichen Logger 1000000 mal den gleichen Pointer umsonst gespeichert.
-
WartungsProgrammierer schrieb:
Okay, wie sieht denn dann das typische state-of-the-art c++ Beispiel aus?
Falls die Frage an mich gerichtet war: Keine Ahnung. volkard empfiehlt Modern C++ Design zu lesen.
MfG SideWinder
-
bewertung schrieb:
Eigentlich schon, wenn man eine createInstance Methode einführt. Dann fallen auch ein paar andere Probleme wie Initialisierungsreihenfolge weg.
Um das Problem mit der Initialisierungsreihenfolge zu lösen wird man ein Meyers Singleton verwenden. Das bedeutet schonmal ein (zumindest implizites) if am Beginn deiner getInstance() Methode. Aber es kommt noch schlimmer, denn getInstance() muss ja auch Thread-Safe sein. Ein naiver Ansatz wird dabei evtl. ein Mutex verwenden, d.h. jeder getInstance() Aufruf bedeutet ein Lock acquiren. Eine bessere Lösung wird Double Checked Locking verwenden, aber auch da brauchts mindestens ein if und eine Memory-Barrier. Erzähl mir jetzt nicht dass das performanter ist als einfach über eine Referenz zuzugreifen. Abgesehen davon sind diese Probleme die "wegfallen" alles Probleme die mach sich durch die Verwendung von Singleton überhaupt erst eingefangen hat. Mit DI ist es schon systembedingt unmöglich dass es jemals zu einer undefinierten Initialisierungsreihenfolge kommen könnte.
bewertung schrieb:
dot schrieb:
Und wenn du 100000 Objekte hast die alle auf einen anderen Logger loggen können müssen hast du sowieso ganz andere Probleme...
Wieso auf einen anderen, du hast auch beim gleichen Logger 1000000 mal den gleichen Pointer umsonst gespeichert.
Der Punkt ist: Wenn du soviele Objekte hast die was loggen müssen dann hast du imo was falsch gemacht. Abgesehen davon hat ja hustbaer schon eine sehr elegante Lösung mit Thread Locals vorgestellt die in so einem Fall evtl. interessant sein könnte.
-
dot schrieb:
Um das Problem mit der Initialisierungsreihenfolge zu lösen wird man ein Meyers Singleton verwenden. Das bedeutet schonmal ein (zumindest implizites) if am Beginn deiner getInstance() Methode. Aber es kommt noch schlimmer, denn getInstance() muss ja auch Thread-Safe sein. Ein naiver Ansatz wird dabei evtl. ein Mutex verwenden, d.h. jeder getInstance() Aufruf bedeutet ein Lock acquiren. Eine bessere Lösung wird Double Checked Locking verwenden, aber auch da brauchts mindestens ein if und eine Memory-Barrier. Erzähl mir jetzt nicht dass das performanter ist als einfach über eine Referenz zuzugreifen. Abgesehen davon sind diese Probleme die "wegfallen" alles Probleme die mach sich durch die Verwendung von Singleton überhaupt erst eingefangen hat. Mit DI ist es schon systembedingt unmöglich dass es jemals zu einer undefinierten Initialisierungsreihenfolge kommen könnte.
mindestens ein if mit memory barrier braucht man auch für's serialisieren der anfragen der parallelen threads, die auf den selben logger schreiben wollen. das kann der singleton in einem aufwasch mitwaschen (singleton steht minderstens seit alexandrescu nicht für eine bestimmte implementierung, sondern für ein konzept, das tausende von ausprägungen haben kann, durch kombination der policies, die man hineinschubst). wer die instance hält, ist auch der einzige schreibberechtigte. das klingt sehr günstig unter der annahme, daß der/die zugrundeliegende(n) streams (z.B. ofstream) von sich aus gar nicht threadsafe sind. weil sich das so natürlich ergibt, hätte ich auch nichts dagegen, es zu verwenden. recht einfach und durchaus performant.
ich will nicht sagen, daß man modern c++ design lesen sollte, und dann noch bessere patentrezepte hat als die GoF liefern. im gegenteil. patentrezepte sind mir nicht koscher. siehe sig. ich sage nicht, man soll immer DI nehmen oder nie oder sowas. ich antworte auch nicht auf die frage, wie man in c++ loggen muß. weil ich es gar nicht weiß.
-
dot schrieb:
bewertung schrieb:
Eigentlich schon, wenn man eine createInstance Methode einführt. Dann fallen auch ein paar andere Probleme wie Initialisierungsreihenfolge weg.
Um das Problem mit der Initialisierungsreihenfolge zu lösen wird man ein Meyers Singleton verwenden. Das bedeutet schonmal ein (zumindest implizites) if am Beginn deiner getInstance() Methode. Aber es kommt noch schlimmer, denn getInstance() muss ja auch Thread-Safe sein. Ein naiver Ansatz wird dabei evtl. ein Mutex verwenden, d.h. jeder getInstance() Aufruf bedeutet ein Lock acquiren. Eine bessere Lösung wird Double Checked Locking verwenden, aber auch da brauchts mindestens ein if und eine Memory-Barrier. Erzähl mir jetzt nicht dass das performanter ist als einfach über eine Referenz zuzugreifen. Abgesehen davon sind diese Probleme die "wegfallen" alles Probleme die mach sich durch die Verwendung von Singleton überhaupt erst eingefangen hat. Mit DI ist es schon systembedingt unmöglich dass es jemals zu einer undefinierten Initialisierungsreihenfolge kommen könnte.
Stellst du dich so dumm, weil du DI als Patentlösung ansiehst und alles andere als schlecht darstellen willst oder verstehst du wirklich nicht warum ich createInstance schreibe?
Die createInstance Methode erstellt das Ding und wird einmal am Anfang aufgerufen, ähnlich wie du es mit DI machst. getInstance macht nur noch return m_instance, vielleicht noch ein assert, sonst nix.
-
Und wer garantiert dir dann, daß createInstance() aufgerufen wird, bevor der erste Anwender versucht, sich mit getInstance() das Singleton-Objekt zu holen?
-
bewertung schrieb:
Stellst du dich so dumm, weil du DI als Patentlösung ansiehst und alles andere als schlecht darstellen willst [...]
Ich seh DI nicht als Lösung für alles, absolut nicht. Mir gehts hier darum aufzuzeigen warum Singleton eine schlechte Idee ist und nicht darum DI als goldenen Hammer anzupreisen...
-
CStoll schrieb:
Und wer garantiert dir dann, daß createInstance() aufgerufen wird, bevor der erste Anwender versucht, sich mit getInstance() das Singleton-Objekt zu holen?
Der Anwender. Sonst gibts halt ein assert oder Crash. Ich kann auch bei DI Müll programmieren, wenn ich NULL übergebe oder zwei "Singletons" erstelle...
-
Also gut, sagen wir mal ich will deinen Singleton für einen Logger verwenden und unter anderem die Konstruktion meiner Objekte loggen lassen.
a) wo muß ich den createInstance()-Aufruf unterbringen, damit ein globales Objekt erfolgreich (d.h. ohne assert/Crash) dieser Klasse angelegt werden kann.
b) Wer ist für den createInstance()-Aufruf zuständig, wenn mehrere Klassen diesen Logger verwenden wollen? Und was passiert, wenn der Aufruf mehrfach passiert?PS: Ich will hier nicht die Vorteile der DI verteidigen, dazu kenne ich das Konzept zu wenig.
-
bewertung schrieb:
Ich kann auch bei DI Müll programmieren, wenn ich NULL übergebe [...]
Müll programmieren kann man immer und überall, vor allem in C++. Aber es ist gerade einer der großen Vorteile von DI dass es ziemlich schwer wird. Das System macht sämtliche Abhängigkeiten explizit, du kannst Objekte gar nicht erst erzeugen ohne essentielle Abhängigkeiten bereitzustellen, d.h. die korrekte Initialisierungsreihenfolge wird schon zur Compiletime forciert. Wenn ein Objekt unbedingt ein anderes braucht dann verlangt es eine Referenz und keinen Pointer. Damit kannst du nicht einfach 0 übergeben (ohne undefiniertes Verhalten in kauf zu nehmen).
bewertung schrieb:
[...] oder zwei "Singletons" erstelle...
Was genau meinst du damit!?
-
CStoll schrieb:
PS: Ich will hier nicht die Vorteile der DI verteidigen, dazu kenne ich das Konzept zu wenig.
Doch kennst du, ich versteh blos nicht, dass es unter eigenen Begiff gehypt wird
-
Zeus schrieb:
Doch kennst du, ich versteh blos nicht, dass es unter eigenen Begiff gehypt wird
Ja, ich wusste bis vor kurzem auch nicht dass es einen Namen dafür gibt. Mich hats auch gewundert dass es da offenbar einen gewissen Hype drum gibt, ich dachte eigentlich dass das für jeden vernünftigen Programmierer die natürliche Vorgehensweise wäre. Aber seit ich es weiß verwend ich den Namen auch, hat den Vorteil dass sofort klar ist was gemeint ist...