Singleton Problem



  • 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.



  • xdrectfvzgbh schrieb:

    Jetzt wirds mal interessanter. Die Singleton Diskussion ist sowieso langweilig und dreht sich im Kreis.

    Wenn du die Diskussion um Unit-Tests vertiefen willst, mach einen neuen Thread auf. Über die Unternehmensstruktur meines Arbeitgebers muss ich mich natürlich bedeckt halten; hier nur so viel: Selbstverständlich schreibe ich Mock-Klassen, wo das zum Testen Sinn macht (sprich: wo Klassen polymorphisch verwendet werden). Ebenso selbstverständlich schreibe ich all meinen Code so, dass er prinzipiell ohne größere Probleme an anderer Stelle erneut verwendet werden könnte -- ganz unabhängig davon, ob der Fall später tatsächlich eintritt, gebietet das schon die Wartbarkeit, denn deine Anwendung muss hinterher erweiterbar sein. Wenn mal zusätzlich Daten aus einer zweiten Datenquelle verarbeitet werden sollen, stehst du ziemlich dumm da, wenn nicht nur deine Datenbankverbindungsklasse ein Singleton ist, sondern sich auch all dein Code darauf verlässt, dass das der Fall ist. Und erfahrungsgemäß tritt der Fall, dass Code wiederverwendet wird, in dem Maße häufiger auf, in dem dein Code wiederverwendbar ist. Das dürfte in die Kategorie der Dinge fallen, die ziemlich offensichtlich sind, nachdem sie einmal erwähnt wurden.

    Im Übrigen scheint dir nicht ganz klar zu sein, was dependency injection bedeutet. Es hat nichts mit dem Herumreichen von Referenzen auf bestehende Objekte zu tun (und schon gar nicht mit Singletons, ganz im Gegenteil!), sondern dreht sich darum, die konkrete Klasse von Datenmembern einer Klasse nicht in dieser Klasse festzulegen, sondern einem Injektor zu überlassen, der die Abhängigkeit dann zur Laufzeit injiziert. Das ist in C++ eine sehr krampfige Angelegenheit und eher selten eine gute Idee; es ist nicht völlig unmöglich, aber im Grunde braucht man dafür Zeigersemantik und eine VM mit Reflection.



  • seldon: Falsch. Dependency Injection != Using an IoC container



  • Ich lass mal einen Link auf http://martinfowler.com/articles/injection.html da; da kommt der Begriff nämlich her. Du solltest dir insbesondere den Abschnitt "Inversion of Control" mal durchlesen, wo der Autor erklärt, wie der Zusammenhang zwischen dependency injection und inversion of control ist. Spoiler: DI ist eine Art von IoC.


Anmelden zum Antworten