Singleton Problem



  • Ich hoffe wir reden jetzt nicht aneinander vorbei. Der Zugriffsfunktionen auf die DLL-Funktionen in die Singletonklasse zu packen lag lediglich nahe. Der eigentliche Zweck des Singletons ist aber sicherzustellen (nichts anderes macht MyDLL::GetInstance()), dass _dllFunktion ein gültiger Funktionspointer ist. Deine Lösung erfordert IMHO wenigstens noch ein if um die _dllFunktion herum. Wir bewegen uns hier aber wohl schon im Bereich des persönlichen Geschmacks.



  • Ich finde die Diskussion hier nicht zielführend. Warum? Weil hier überhaupt keine Basis dafür besteht, welche Nachteile Singletons haben ("Das weiß doch jeder" ist nicht adäquat, denn das stimmt 1. nicht und 2. braucht man dennoch eine gemeinsame Basis).

    Ob Singleton in bestimmten Situationen dann ein Anti-Pattern ist oder nicht, spiegelt sich dann darin wieder, ob die Nachteile dort noch gelten. Das wäre Mal ein systematischer Ansatz.

    Nehmen wir Mal an, wikipedia wäre eine gute Quelle und nehmen die Nachteile:
    http://de.wikipedia.org/wiki/Singleton_(Entwurfsmuster)#Nachteile

    - Äquivalent zu globalen Variablen erstellen
    => globale Variablen sind der der Alternativvorschlag hier, fällt also weg

    - Verschleierte Abhängigkeit + Kopplung
    => Massive Argumente, trifft beides auch eingeschränkt auf globale Variablen zu, wobei die wenigstens im Kopf der Quellcodedateien auftauchen

    - Einzigartigkeit in MT-Umgebungen nicht gegeben (wurde ja oben bereits diskutiert)

    - Das Testen eines Singleton kann kompliziert sein.
    => Das ist ein Nachteil

    - Achtung bei Initialisierungs-Reihenfolge
    => ebenso bei globalen Variablen

    - Eine Ressource-Deallokation von Ressourcen, die das Singleton verwendet, ist schwierig
    => Ist hier vermutlich egal, wäre bei einer globalen Variable aber wohl ähnlich

    Oder kurz:
    Was ist an einer globalen Variable so viel besser? Singletons als Anti-Patterns zu verneinen, aber globale Variablen einzuführen erfordert mMn eine gute Begründung.



  • @Morle: In dem Fall etwas wie (aufs Wesentliche zusammengestrichen)

    // Header
    namespace MyDLL {
      void Funktion(void *p);
    }
    
    ...
    
    // Source
    namespace MyDLL {
      namespace {
        struct dirty_dll_handler {
          dirty_dll_handler(char const *dll_name, int flags) {
            dll_handle = dlopen(dll_name, flags);
            dllFunktion = static_cast<void(*)(void*)>(dlsym(dll_handle, "dllFunktion"));
          }
    
          Mutex mLock;
    
          void *dll_handle;
          void (*dllFunktion)(void*);
        };
    
        dirty_dll_handler global_data("old_dirty.dll", RTLD_LAZY);
      }
    
      void Funktion(void *p) {
        Lock lock(global_data.mLock);
        global_data._dllFunktion(p);
      }
    }
    

    Ein Singleton ist da weder notwendig noch vorteilhaft. Die Klasse ist auch logisch kein Singleton, weil sie sich, wenn du mehrere solcher alten DLLs abhandeln musst, wiederverwenden ließe.

    @Eisflamme: An einer globalen Variable ist nicht wirklich was besser als an einem Singleton. Es ist umgekehrt auch nicht wirklich was besser an einem Singleton als an einer globalen Variable. Wie gesagt, das Problem müsste eigentlich in der DLL gelöst werden, die selbst globale Variablen zu halten scheint.



  • Wie macht ihr Logging, wenn nicht als Singleton und es möglich sein soll, dass mehrere Thread ins gleiche Logfile schreiben, ohne das die Messages zerstückelt werden und das Logfile soll bis zum beenden immer geöffnet sein, damit keiner die Schreibrechte klaut? Eine Logging Klasse in jede andere Klasse durchzureichen ist doch total umständlich.



  • Man nehme ein globals std::ostream Objekt?



  • Nathan schrieb:

    Man nehme ein globals std::ostream Objekt?

    Also sowas wie std::cout!? Wieso sollte sowas ein Singleton sein?



  • Nathan schrieb:

    Man nehme ein globals std::ostream Objekt?

    Also wieder globale Variablen, statt Singletons. Weil Singletons jetzt böse sind und globale Variablen es früher waren, aber das schon alle vergessen haben.



  • Singleton und globale Variablen sind zwei völlig voneinander unabhängige Dinge...



  • dot schrieb:

    Singleton und globale Variablen sind zwei völlig voneinander unabhängige Dinge...

    Nein, in C++ zumindest kann man keine Singletons ohne globale/statische Variablen implementieren. Hatten wir übrigens schon 🙂

    Oder anders ausgedrückt: Das Singleton-Pattern ist nicht die logische Konsequenz daraus, eine Klasse nur einmal instanziieren zu wollen. Zusätzlich muss auch das Bedürfnis nach einem gemeinsamen Zugriffspunkt bestehen.



  • dot schrieb:

    Nathan schrieb:

    Man nehme ein globals std::ostream Objekt?

    Also sowas wie std::cout!? Wieso sollte sowas ein Singleton sein?

    Ist eine globale Variable ein Singleton?
    Bleiben wir beim Logger, std::cout, std::clog, std::cerr + eigener Logger sind alles Instanzen von std::ostream. Nach einer Instanz sieht das nicht gerade aus.

    looogggeer schrieb:

    Nathan schrieb:

    Man nehme ein globals std::ostream Objekt?

    Also wieder globale Variablen, statt Singletons. Weil Singletons jetzt böse sind und globale Variablen es früher waren, aber das schon alle vergessen haben.

    Wahl zwischen Pest und Cholera. :p

    Nein, std::cout ist eine begründete globale Variable. Da gibt es auch keine Alternative, weil std::ostream kein Singleton ist.



  • Ach, hier stand Käse.



  • @Morle
    Jo.
    Dein Beispiel fällt für mich in die selbe Kategorie wie auch non-nesting Init/Release Funktionen.

    Bei sowas finde ich nen "Singleton" als Workaround OK. Wobei ich hier auch normale freie Funktionen als Wrapper vorziehen würde.
    Irgendwo muss es aber irgend ein "Singleton" geben, weil es darf ja nur eine Mutex geben. (Wobei dieses "Singleton" nicht unbedingt das "Singleton-Pattern" verwenden muss, es tut's auch eine globale Variable + call_once o.ä.)

    Welche Dinge als Workaround OK sind oder nicht finde ich aber nicht wirklich ein sehr spannendes Thema. Was sein muss muss eben sein.

    Hast du auch ein Beispiel wo ein Singleton vorkommt das nicht Teil eines Workarounds ist?

    pS: Bei deinem Beispiel einen Zeiger auf irgend eine Wrapper-Klasse rumzureichen wäre mMn. komplett verkehrt. Würde nur einen weiteren unnötigen Bruchpunkt schaffen.



  • Nexus schrieb:

    Oder anders ausgedrückt: Das Singleton-Pattern ist nicht die logische Konsequenz daraus, eine Klasse nur einmal instanziieren zu wollen.

    Natürlich nicht, das hab ich auch nie behauptet, ganz im Gegenteil. Zweck des Singleton Pattern ist es, auszudrücken, dass es sich um eine prinzipielle Eigenschaft eines Typs handelt, dass es immer nur eine Instanz dieses Typs geben kann (vgl. mit dem gleichnamigen Konzept aus der Mathematik). Der globale Zugriffspunkt (der streng genommen übrigens eigentlich nicht global ist, da er nicht globalen Scope hat; aber ich denk, wir wissen beide, was damit gemeint ist) ist imo lediglich als Konsequenz der üblichen Implementierung eines Singleton zu sehen und auf keinen Fall ist das Bedürfnis nach einem globalen Zugriffspunkt hinreichende Bedingung für die Anwendung des Singleton Pattern. Das Wesen des Singleton steckt in der Konstruktionsbeschränkung; die ist, was ein Singleton ausmacht, ohne die gibt's per Definition kein Singleton. Und Konstruktionsbeschränkung und globaler Zugriffspunkt sind zwei völlig orthogonale Konzepte...

    Nathan schrieb:

    dot schrieb:

    Nathan schrieb:

    Man nehme ein globals std::ostream Objekt?

    Also sowas wie std::cout!? Wieso sollte sowas ein Singleton sein?

    Ist eine globale Variable ein Singleton?

    Wie oben geschrieben sind "globale Variable" und "Singleton" unabhängige Konzepte. Eine globale Variable ist ein Objekt, als Singleton bezeichnet man dagegen einen Typ mit bestimmten Eigenschaften. Natürlich könnte der Typ einer globalen Variable rein prinzipiell ein Singleton sein, auch wenn es in C++ vermtulich keinen Weg gibt, sowas direkt zu bauen. Aber weder "ist" eine globale Variable notwendigerweise ein Singleton, noch ist die eine Instanz eines Singleton notwendigerweise eine globale Variable...



  • dot schrieb:

    xdrectfvzgbh schrieb:

    dot schrieb:

    Die richtige Frage ist eher, was es genau für einen Grund gibt, aus dem sichergestellt werden muss, dass die fragliche Klasse um jeden Preis nur einmal instanziert werden kann. Oder anders ausgedrückt: Sag mir, wieso es in der Geschichte der Menschheit nie jemals ein zweites Objekt dieser Klasse wird geben dürfen.

    Du hast Singleton nicht verstanden. Das hat nichts mit der Geschichte der Menschheit zu tun, sondern nur mit der Programminstanz. Darum findest du auch nie einen Anwendungsfall.

    Ich bin ganz Ohr, vielleicht kannst du mir und den anderen Unwissenden hier ja die Augen öffnen!?

    Jedes Ding, dass es für eine spezielle Anwendung nur einmal geben soll und von dem jeder Entwickler wissen soll, dass er hier genau dieses verwenden soll ist ein Singleton. Wenn bestimmte Resourcen nur einmal geladen werden sollen, mache ich ein Singleton, dass diese cached (das bedeutet nicht, jeder Cahce ist ein Singleton (ja ich weiß, du willst alles falsch verstehen)). Du wirts diesen Cache zwar viel lieber an 10000 Klassen weiter geben und dort als Member halten, aber das machen dann sowieos wieder andere Entwickler falsch und erstellen sich ihren eigenen Cache, was dem Anwendungskonzept widerspricht. Aber du entwickelst sowieso nur allein, stimmst?



  • Wie machst du das dann, wenn du Unit-Tests schreibst oder den selben Code mal in mehreren Anwendungen verwenden willst?

    Und wenn du mit Entwicklern arbeitest, die es nicht hinkriegen, ein übergebenes Objekt zu verwenden, kannst du dich dann wirklich darauf verlassen, dass die mit deinem Wald von Singletons zurechtkommen? Mit Verlaub, dieses Konzept klingt wie die Rückkehr zur schlechten alten Zeit vor der Erfindung der Objektorientierung.



  • dot schrieb:

    Nathan schrieb:

    dot schrieb:

    Nathan schrieb:

    Man nehme ein globals std::ostream Objekt?

    Also sowas wie std::cout!? Wieso sollte sowas ein Singleton sein?

    Ist eine globale Variable ein Singleton?

    Wie oben geschrieben sind "globale Variable" und "Singleton" unabhängige Konzepte. Eine globale Variable ist ein Objekt, als Singleton bezeichnet man dagegen einen Typ mit bestimmten Eigenschaften. Natürlich könnte der Typ einer globalen Variable rein prinzipiell ein Singleton sein, auch wenn es in C++ vermtulich keinen Weg gibt, sowas direkt zu bauen. Aber weder "ist" eine globale Variable notwendigerweise ein Singleton, noch ist die eine Instanz eines Singleton notwendigerweise eine globale Variable...

    Unsinniger Formalismus.

    Globale Variable und Singleton sind was alle relevanten Punkte betrifft identisch.

    Ausschlaggebend ist einzig und alleine das Highlander-Prinzip. Ob das auf einen ganzen Typ angewendet wird, oder bloss eine Instanz eines Typs die zu einem bestimmten Zweck verwendet wird ist in Wirklichkeit egal.



  • Oh man hier geht ja die Post ab.
    Hab das meiste jetzt nur überflogen, ohne großartige Erkenntnisse.

    Aber ist auch nicht so schlimm. Ich hab erstmal eine zufrieden stellende Lösung gefunden.



  • Und wenn du mit Entwicklern arbeitest, die es nicht hinkriegen, ein übergebenes Objekt zu verwenden, kannst du dich dann wirklich darauf verlassen, dass die mit deinem Wald von Singletons zurechtkommen?

    Bei einem Singleton weiß jeder Entwickler, dass es nur eines geben soll und er erstellt kein zweites bzw kann es garnicht. Bei einer Klasse die eigentlich ein Singleton sein sollte, aber nicht als solche implementiert ist, sondern nur als per DI an alle Klasse weitergegeben wird weiß nicht jeder Entwickler, dass er irgendwo die übergebene Klasse herbekommen und verwenden muss und sich nicht seine eigene erstellen soll.

    seldon schrieb:

    Wie machst du das dann, wenn du Unit-Tests schreibst oder den selben Code mal in mehreren Anwendungen verwenden willst?

    Jetzt wirds mal interessanter. Die Singleton Diskussion ist sowieso langweilig und dreht sich im Kreis. Erzähl mal wie du das machst. Schreibst du für unittests für alle Klasse die nicht zum Modul gehören Mocks? Wieviel deines Codes verwendest du in mehreren Anwendungen? Wie verschieden sind die Anwendungen? Was ist das für Code den du wiederverwendest, sowas wie allgemeine Listen, und Bildverarbeitungsalgorithmen usw. oder was anwendungsspezifisches? Wieviele Leute arbeiten mit dem Code?



  • hustbaer schrieb:

    Globale Variable und Singleton sind was alle relevanten Punkte betrifft identisch.

    So lange du mit Fällen zu tun hast, wo dies Zutrifft, kann es sich von vornherein schon nicht um potentielle Anwendungsfälle für das Singleton Pattern handeln, da keine Forderung nach einer Konstruktionsbeschränkung des Typs besteht und die notwendigen Voraussetzungen für den Einsatz eines Singleton damit niemals erfüllt sein können...

    Oder anders ausgedrückt: So lange es keinen Unterschied machen würde, ob du eine globale Variable oder ein Singleton verwendest, ist Singleton unangebracht...



  • Bei einem Singleton weiß jeder Entwickler, dass es nur eines geben soll und er erstellt kein zweites bzw kann es garnicht.

    Richtig, aber man muss aufpassen, dass man es nicht übertreibt. Wenn es sehr viele Singletons gibt, ist die Wiederverwendbarkeit enorm eingeschränkt, Unit-Tests sind sehr umständlich zu schreiben und Abhängigkeiten werden verschleiert, weil anhand der Schnittstelle einer Klasse nicht mehr deutlich wird, wie diese sind. DI mag etwas mehr Aufwand sein, aber es löst diese Probleme.

    Bei einer Klasse die eigentlich ein Singleton sein sollte, aber nicht als solche implementiert ist, sondern nur als per DI an alle Klasse weitergegeben wird weiß nicht jeder Entwickler, dass er irgendwo die übergebene Klasse herbekommen und verwenden muss und sich nicht seine eigene erstellen soll.

    Beispiel? Ich finde nicht, dass selbst erstellen eine Alternative ist. Wenn einen Teil der Software erweitere und dort ein Objekt einer Klasse nutzen möchte, dann überlege ich mir, wo die Klasseninstanz logisch hingehört. Sind Daten davon auch auf höherer Ebene notwendig? Dann ist wohl logisch, dass das Objekt auf der höheren Ebene ansässig ist.

    hustbaer:
    Du meinst, Singletons sollte man dann ggü. globalen Variablen einsetzen, wenn man Exklusivität durchsetzen möchte? Wie wenn ein Singleton z.B. die Schnittstelle zu einem Drucker darstellt (unter Annahme, dass der einfach jedes ihm gesendete Zeichen sofort druckt)?

    Bei so klaren Auffassungen über die (Nicht-)Anwendbarkeit dieses Patterns sollte es doch einfach sein Mal ein paar klare Regeln zu formulieren. Darüber könnte man sich Mal strukturiert unterhalten.


Anmelden zum Antworten