Datenbankzugriffsschicht ohne mit Singletons um sich zu schmeißen?
-
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...
-
CStoll schrieb:
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?Relativ am Anfang von main, so ähnlich wie wenn du ihn mit DI überall hin weitergeben willst.
Und was passiert, wenn der Aufruf mehrfach passiert?
Assert, crash, nix, was man halt in die createInstance() rein macht.
dot schrieb:
bewertung schrieb:
[...] oder zwei "Singletons" erstelle...
Was genau meinst du damit!?
// main Logger loggerDerEigentlichDerEinzigeSeinSoll. foo(loggerDerEigentlichDerEinzigeSeinSoll) //... viel Code //... ganz anderer Klasse Logger loggerDerZweite. foo(loggerDerZweite)
-
Mit DI werden die Objekte im Normalfall von einem Framework erzeugt und nicht von dir selbst, dementsprechend wirst du nie unabsichtlich zwei erzeugen.
MfG SideWinder
-
bewertung schrieb:
dot schrieb:
bewertung schrieb:
[...] oder zwei "Singletons" erstelle...
Was genau meinst du damit!?
// main Logger loggerDerEigentlichDerEinzigeSeinSoll. foo(loggerDerEigentlichDerEinzigeSeinSoll) //... viel Code //... ganz anderer Klasse Logger loggerDerZweite. foo(loggerDerZweite)
Ähm, ich hoffe dir ist klar dass die Tatsache dass genau das problemlos möglich ist doch gerade einer der wesentlichen Vorteile von DI ist!? Abgesehen davon seh ich da keine Singletons...
-
bewertung schrieb:
CStoll schrieb:
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?Relativ am Anfang von main, so ähnlich wie wenn du ihn mit DI überall hin weitergeben willst.
Dir ist klar, daß globale Objekte angelegt werden, bevor du in die main() kommst? Und daß der Autor der main()-Funktion möglicherweise gar nicht weiß, daß ich tief in den Innereien meiner Library den Singleton-Logger verwende?
Und was passiert, wenn der Aufruf mehrfach passiert?
Assert, crash, nix, was man halt in die createInstance() rein macht.
Also muß diese Funktion genauso geschützt werden wie die getInstance() bei einem "normalen" Singleton. Aber dadurch, daß sie explizit aufgerufen wird, vergrößerst du nur die Probleme des Singleton, ohne damit einen erkennbaren Nutzen zu bieten.
@Zeus/dot: Kann schon sein, daß ich das bereits unbewußt angewendet habe. Darüber muß ich noch etwas meditieren.
-
SideWinder schrieb:
Mit DI werden die Objekte im Normalfall von einem Framework erzeugt und nicht von dir selbst, dementsprechend wirst du nie unabsichtlich zwei erzeugen.
MfG SideWinder
dot schrieb:
bewertung schrieb:
dot schrieb:
bewertung schrieb:
[...] oder zwei "Singletons" erstelle...
Was genau meinst du damit!?
// main Logger loggerDerEigentlichDerEinzigeSeinSoll. foo(loggerDerEigentlichDerEinzigeSeinSoll) //... viel Code //... ganz anderer Klasse Logger loggerDerZweite. foo(loggerDerZweite)
Ähm, ich hoffe dir ist klar dass die Tatsache dass genau das problemlos möglich ist doch gerade einer der wesentlichen Vorteile von DI ist!? Abgesehen davon seh ich da keine Singletons...
Sell dich nicht so...?!?!?!?
CStoll schrieb:
bewertung schrieb:
CStoll schrieb:
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?Relativ am Anfang von main, so ähnlich wie wenn du ihn mit DI überall hin weitergeben willst.
Dir ist klar, daß globale Objekte angelegt werden, bevor du in die main() kommst? Und daß der Autor der main()-Funktion möglicherweise gar nicht weiß, daß ich tief in den Innereien meiner Library den Singleton-Logger verwende?
Dann würde auch die von dot verwendete Constructor-Injection nicht gehen.
Und was passiert, wenn der Aufruf mehrfach passiert?
Assert, crash, nix, was man halt in die createInstance() rein macht.
Also muß diese Funktion genauso geschützt werden wie die getInstance() bei einem "normalen" Singleton. Aber dadurch, daß sie explizit aufgerufen wird, vergrößerst du nur die Probleme des Singleton, ohne damit einen erkennbaren Nutzen zu bieten.
Nutzen siehe oben.
Ich hab jetzt keine Lust mehr alles dreimal zu erklären.
-
bewertung schrieb:
Sell dich nicht so...?!?!?!?
bewertung schrieb:
Dann würde auch die von dot verwendete Constructor-Injection nicht gehen.
Wo liegt das Problem mit der Injection?
bewertung schrieb:
Ich hab jetzt keine Lust mehr alles dreimal zu erklären.
Geh doch einfach auf die genannten Punkte ein...
-
bewertung schrieb:
Dir ist klar, daß globale Objekte angelegt werden, bevor du in die main() kommst? Und daß der Autor der main()-Funktion möglicherweise gar nicht weiß, daß ich tief in den Innereien meiner Library den Singleton-Logger verwende?
Dann würde auch die von dot verwendete Constructor-Injection nicht gehen.
Wieso das? In meiner Library kann ich machen, was ich will, also kann ich auch sowas schreiben:
Logger myLog(...); MyClass Worker(myLog);
Da muß ich mich nicht davon abhängig machen, daß irgendjemand anderes (der vielleicht gar nichts von der inneren Arbeitsweise meines Codes weiß) mir einen Logger zur Verfügung stellt bzw. initialisiert?
Und was passiert, wenn der Aufruf mehrfach passiert?
Assert, crash, nix, was man halt in die createInstance() rein macht.
Also muß diese Funktion genauso geschützt werden wie die getInstance() bei einem "normalen" Singleton. Aber dadurch, daß sie explizit aufgerufen wird, vergrößerst du nur die Probleme des Singleton, ohne damit einen erkennbaren Nutzen zu bieten.
Nutzen siehe oben.
Der Nutzen besteht darin, daß die getInstance()-Methode sich verkürzt und keine Synchronisation etc benötigt, habe ich das richtig verstanden?
Dafür benötige ich jetzt eine andere Methode, die ihre Synchronisation- und ähnlichen Probleme erbt - und muß auch noch dafür sorgen, daß jemand anderes diese Methode aufruft. Irgendwie habe ich das Gefühl, daß da die Nachteile überwiegen.
(wobei ich nichtmal ein Verfechter von "normalen" Singletons bin - ich bin schon oft genug über global gültige Variablen in den unterschiedlichsten Verkleidungen gestolpert)
-
Zeus schrieb:
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
Gehypt wird glaub ich mehr DI mit Frameworks und entsprechenden Konfigurationsmöglichkeiten und nicht die simple Konstruktor-Injection die dot hier so hypt.