Schreibweise bei Singletons



  • Nur damit keine Missverständnisse auftreten, ein Singleton dient als Interface für eine Klasse. Wenn Klassen ausschließlich als Singleton konzipiert sind, zeigt das meistens einen Design-Fehler.
    Weiterhin benutzt du das Meyers-Singleton, ich persönlich empfehle das Alexandrescu-Singleton.

    In beiden Fällen sieht die get-Instance-Function dann so aus:

    class WhatEver
    {
      WhatEver& getInstance() { return Singleton<WhatEver>::getInstance(); }
    }
    

    Dann kannst du das Pattern auch in anderen Klassen nutzen und verschmutzt die WhatEver-Klasse nicht unnötig.



  • nurf schrieb:

    Nur damit keine Missverständnisse auftreten, ein Singleton dient als Interface für eine Klasse. Wenn Klassen ausschließlich als Singleton konzipiert sind, zeigt das meistens einen Design-Fehler.

    Exakt! Wenn es eine globale Instanz nicht tut, ist etwas faul. Singletons sind zwar in C++ möglich, aber auch ungewollt. Ich kann nur empfehlen, es einfach zu lassen.



  • ChristophLu schrieb:

    Naja auf Singletons verzichten geht meiner Meinung nach nicht.

    Dazu sag ich nur dass ich in all den Jahren bisher zwei oder drei rein hypothetische Beispiele gesehen hab wo ein Singleton unter anderem sinnvoll und richtig eingesetzt werden könnte. Ein realer Fall wo der Einsatz eines Singleton tatsächlich korrekt und sinnvoll gewesen wäre ist mir noch nie untergekommen. Und ich hab schon tausende reale Singletons gesehnen. Und jedes einzelne davon war ein grober Designfehler.
    Das Problem mit Singleton ist dass es die Dinge auf den ersten Blick so einfach macht, weswegen es so gerne verwendet wird, auch wenn in 99,99% dieser Fälle bei genauerer Betrachtung die Voraussetzungen für den Einsatz eines Singleton gar nicht gegeben sind. Eines der ganz großen Probleme hast du ja selbst schon ausgemacht, wenn auch nicht als potentielles Problem identifiziert:

    ChristophLu schrieb:

    Auf diese wird von eigentlich fast allen anderen Klassen zugegriffen.

    Meiner Erfahrung nach ist es notwendiges Merkmal von gutem Design dass genau sowas nicht auftritt. Ein Singleton ist die perfekte Lösung um Abhängigkeiten unter den Tisch zu kehren da der Code mit Singleton nichtmehr gezwungen ist Abhängigkieten explizit widerzuspiegeln. Das eigentliche Problem (zuviele Abhängigkeiten) wird durch ein Singleton also nicht gelöst sondern lediglich die Symptome behandelt. Früher oder später kommt man dann erfahrungsgemäß aber an den Punkt wo sich das Problem an irgendeiner Stelle wieder bemerkbar macht und man nun schlussendlich gewzungen ist das Singleton durch eine richtige Lösung zu ersetzen. Dies gestaltet sich nun aber extrem aufwändig da das Singleton inzwischen genug Zeit gehabt hat um still und leise in praktisch alle Teile des Codes zu metastasieren. Auch ich war schon das ein oder andere Mal versucht ein Singelton zu verwenden, und ich war bisher jedesmal später froh dass ich mich dagegen entschieden habe da ich noch in jedem dieser Fälle an einen Punkt gekommen bin wo das Singleton zu einem erheblichen Problem geworden wäre. Für mich ist Singleton jedenfalls definitiv ein Antipattern...



  • Wie würdet ihr z.B. einen Cache für eine Datenbank schreiben, wenn man den Cache überall verwenden soll, wo auf die DB zugeriffen wird und wenn es kein Singleton sein soll?

    (Der DB-Cache könnte auch ein TextureManager oder sonst ein ResourceManager sein).



  • WieEsGeht schrieb:

    Wie würdet ihr z.B. einen Cache für eine Datenbank schreiben, wenn man den Cache überall verwenden soll, wo auf die DB zugeriffen wird und wenn es kein Singleton sein soll?

    Mit Datenbanken hatte ich noch nie viel zu tun aber ich erinnere mich an eine Vorlesung über Designpatterns wo wir mal eine Diskussionsrunde über Singletons eingelegt haben und da waren Webentwickler ganz weit vorne dabei bei denen die von ihren leidvollen Erfahrungen mit Singleton für Datenbanken berichtet haben, scheint in diesem Bereich wohl ein sehr gängiges Problem zu sein. Ich weiß jetzt nicht genau wie sowas konkret ausschauen würde aber rein intuitiv würde ich mal meinen der Datenbankcache ist eigentlich etwas was in die Datenbank sollte und nicht irgendwo also globales Objekt rumschwirren. Und die Datenbank würde ich wohl auch als eigenes Objekt anlegen und alles was die Datenbank verwendet hält naturgemäß eine Referenz auf die Datenbank. Einfach, effektiv und ganz ohne Singleton...



  • WieEsGeht schrieb:

    Wie würdet ihr z.B. einen Cache für eine Datenbank schreiben, wenn man den Cache überall verwenden soll, wo auf die DB zugeriffen wird und wenn es kein Singleton sein soll?

    (Der DB-Cache könnte auch ein TextureManager oder sonst ein ResourceManager sein).

    Via Dependecy Injection reinbringen...



  • Wir können auch TextureManager statt Datenbank nehmen. Das Problem ist doch, dass ich irgendwas nicht zweimal laden will, sondern die schon geladene Ressource (Textur, Daten) verwenden will. Dazu brauche ich irgendwo genau eine Instanz die ich fragen kann, ob die Ressource schon geladen ist. Das hört sich für mich nach Singleton an. Dependecy Injection bringt da doch auch nix.



  • ... das ist hier die Frage 🙂

    Und in der Regel auch der Designfehler. Es geht ja darum, ob etwas nur einmal in einem Prozess existieren darf. Und das ist in den seltensten Fällen so.

    Hinzu kommt, dass man das immer noch über Factory Funktionen à la DBCache& getDBCache(const DBConnection& connection) regeln kann. Ob die intern mit einer einzelnen Instanz oder mehreren arbeiten ist dann für den Rest der Applikation egal.



  • Meiner Meinung nach werden Singletons oft missbraucht um eine Referenz auf die jeweiligen Instanz zu bekommen. Das bringt aber dasselbe Problem wie Globale Variablen mit sich. Singleton's sind ein Pattern um genau eine Instanz zuzulassen, nicht um an sie ranzukommen.



  • Exakt, man kann es gar nicht oft genug sagen. Der Sinn des Singleton Pattern ist genau eben nicht eine globale Instanz eines Objektes zur Verfügung zu stellen. Der Sinn des Singleton Pattern ist es sicherzustellen dass auf keinen Fall und überhaupt irgendwie mehr als genau eine Instanz der fraglichen Klasse erzeugt werden kann. Genau das und nur das ist der Zweck eines Singleton. Die globale Instanz ist lediglich eine Konsequenz des Pattern und nicht das wofür der Pattern gedacht ist. Man könnte jetzt darüber argumentieren inwiefern der Singleton Pattern damit überhaupt den Grundfesten der OOP widerspricht, aber das ist an dieser Stelle egal. Wichtig ist dass Singleton kein Ersatz für eine globale Variable ist, nie dafür gedacht war und ständig genau dafür verwendet wird weil es so schön einfach ist und so viele Programmierer den Pattern nicht verstanden haben.

    Da ich von TextureManagern mehr verstehe (auch wenn ich von expliziten Managerklassen generell nicht viel halte, sowas macht bei mir normalerweise irgendein anderes Objekt implizit) kann ich dir versichern dass es im Allgemeinen eine sehr sehr schlechte Idee wäre einen TextureManager als Singleton anzulegen. Spätestens wenn du mehrere Bildschirme verwenden willst und damit mehrere Fenster und evtl. auch mehrere Grafikkarten verwalten musst wird ein solches Design weit über seine Grenzen strapaziert sein. Ohne Singleton ist sowas in jedem Fall viel sauberer und flexibler gelöst da auch andere Probleme wie z.B. die Frage wann wie initialisiert wird und was passiert wenn jemand vorher drauf zugreifen will gar nicht erst auftreten.
    Und ich bin mir sicher dass es bei Datenbanken nicht großartig anders ausschaut. Man wird zwar sehr oft mit genau einer Datenbank arbeiten aber es wird wohl praktisch nie eine grundlegende Notwendigkeit sein dass die Möglichkeit der Existenz von zwei Datenbank Objekten mit aller Kraft unterbunden werden muss. Genau das ist es aber was man mit einem Singleton ausdrücken würde.



  • dot schrieb:

    Da ich von TextureManagern mehr verstehe (auch wenn ich von expliziten Managerklassen generell nicht viel halte, sowas macht bei mir normalerweise irgendein anderes Objekt implizit) kann ich dir versichern dass es im Allgemeinen eine sehr sehr schlechte Idee wäre einen TextureManager als Singleton anzulegen. Spätestens wenn du mehrere Bildschirme verwenden willst und damit mehrere Fenster und evtl. auch mehrere Grafikkarten verwalten musst wird ein solches Design weit über seine Grenzen strapaziert sein.

    Der TextureManager war auch für Files gedacht, damit man nicht x-mal texture1.bmp einliest. Der soll keine grafikkartenspezifische Texture halten.

    Ohne Singleton ist sowas in jedem Fall viel sauberer und flexibler gelöst da auch andere Probleme wie z.B. die Frage wann wie initialisiert wird und was passiert wenn jemand vorher drauf zugreifen will gar nicht erst auftreten.

    Wieso sollten die nicht auftreten? Du musst auch ohne Singleton deine Klasse irgendwann erzeugen und schauen wer wann und wo diese verwenden kann.



  • WieEsGeht schrieb:

    Wieso sollten die nicht auftreten? Du musst auch ohne Singleton deine Klasse irgendwann erzeugen und schauen wer wann und wo diese verwenden kann.

    Natürlich. Und ohne Singleton erzeuge ich meinen Texture Manager genau dann wenn es geht dort wo es geht. Das impliziert dann z.B. weiter dass Objekte die Texturen laden müssen erst erzeugt werden können wenn ein Texturemanager erzeugt ist da die schließlich eine Referenz drauf brauchen die sie natürlich im Konstruktor benötigen. Ein Singleton würde es erlauben dass ein Objekt auf den TextureManager zugreift bevor dieser noch etwas sinnvolles tun kann. Was soll in so einem Fall passieren? Gibt getInstance() einen Nullpointer zurück? Ist es eine nop? Fliegt eine Exception? Muss ich dann überall wo ich was mit dem TextureManager mache diesen Fehlerfall abfangen? Ohne Singelton stellen sich all diese Fragen gar nicht da eine solche Problemsituation by design unmöglich ist.



  • Gut, dann frag ich einfach mal so:
    Wie würdet ihr denn realisieren, dass x Klassen auf eine bestimmte Instanz zugreifen? In jeder Klasse eine Referenz zu speichern erscheint mir aufwändig. Also sowas ähnliches wie(Pseudocode):

    MyClass::registerErrorStack( ErrorStack &ref );
    
    ErrorStack es;
    MyClassA.registerErrorStack( es );
    MyClassB.registerErrorStack( es );
    

    Oder ist es sowas, was ihr für sinnvoll haltet?



  • Die Antwort ist eigentlich recht simpel... parent Variable... eine ganz gängige Praxis in Qt zum Beispiel...



  • dot schrieb:

    WieEsGeht schrieb:

    Wieso sollten die nicht auftreten? Du musst auch ohne Singleton deine Klasse irgendwann erzeugen und schauen wer wann und wo diese verwenden kann.

    Natürlich. Und ohne Singleton erzeuge ich meinen Texture Manager genau dann wenn es geht dort wo es geht. Das impliziert dann z.B. weiter dass Objekte die Texturen laden müssen erst erzeugt werden können wenn ein Texturemanager erzeugt ist da die schließlich eine Referenz drauf brauchen die sie natürlich im Konstruktor benötigen. Ein Singleton würde es erlauben dass ein Objekt auf den TextureManager zugreift bevor dieser noch etwas sinnvolles tun kann. Was soll in so einem Fall passieren? Gibt getInstance() einen Nullpointer zurück? Ist es eine nop? Fliegt eine Exception? Muss ich dann überall wo ich was mit dem TextureManager mache diesen Fehlerfall abfangen? Ohne Singelton stellen sich all diese Fragen gar nicht da eine solche Problemsituation by design unmöglich ist.

    Ich weiß nicht welchen Fall es geben soll, wo ein TextureManager keine Files laden können sollte, aber wenn wir annehmen, dass es diesen gibt, dann kann ich mit oder ohne Singleton Müll programmieren und den zufrüh erzeugen. Ich kann auch ohne Singleton einen TextureManager irgendwann erzeugen und einem Objekt darauf eine Referenz geben ohne das der TextureManager funktioniert. Ich könnte sogar 100 TextureManager ohne Singleton erzeugen, so dass er im Prinzip sinnlos ist. Müll kann man immer porgrammieren, ob mit oder ohne Singleton. Ob ich einem Objekt eine Referenz übergebe oder sie über getInstance hole, ändert garnichts daran, dass es der falsche Zeitpunkt sein kann. Wenn es so einen Zeitpunkt gibt, bei dem man ein Objekt zufrüh erzeugt, dann sorgst du am besten dafür, dass die Anwendung crasht, vielleicht noch mit assert. Wer sowas programmiert, soll es auch gleich merken, da brauchst du nicht nachdenken ob man überall den Fehler behandeln muss.



  • Dazu brauche ich irgendwo genau eine Instanz die ich fragen kann, ob die Ressource schon geladen ist.

    Dazu muss die Instanz aber kein Singleton sein.



  • knivil schrieb:

    Dazu brauche ich irgendwo genau eine Instanz die ich fragen kann, ob die Ressource schon geladen ist.

    Dazu muss die Instanz aber kein Singleton sein.

    Ja, man kann auch irgendwo genau eine Instanz erstellen und die dann überall rumreichen und verwenden. Im Prinzip wie ein Singleton, nur dass man es nicht so nennt, weil sonst alle Designfehler schreien.


Anmelden zum Antworten