Singleton vs unnamed namespace



  • Du kannst ein Singleton mittels freier Funktionen implementieren. Wer sagt denn, dass ein Singleton immer mit singleton::get_instance() arbeiten muss?



  • Hm. Ok. Mehr Gedanken habt ihr nicht dazu? Das Thema las ich bisher nicht umfassend diskutiert, ist meine Frage so trivial? 😞



  • Eisflamme schrieb:

    Hm. Ok. Mehr Gedanken habt ihr nicht dazu? Das Thema las ich bisher nicht umfassend diskutiert, ist meine Frage so trivial? 😞

    Ich weiß nicht, was hier anders ist als im anderen Thread. Deine Frage war für mich nicht konkreter, sondern schwammiger.



  • Das andere Thema war "namespace vs Klassen" und hier bin ich "Singleton vs unnamed namespaces", rein vom Titel her bin ich hier schon konkreter. Konkret ist meine Frage nicht, aber ich hätte gedacht, da haben viele Gedanken zu von wegen:

    Jaa, also ich mache das immer so, dass ich Singletons fast nie als Klassen benutze, sondern immer namespaces nutze, außer [...]. Und sonst haben unnamed namespaces halt den Vorteil, dass [...], wohingegen...

    Aber anscheinend gibt es zu dem Thema nix in der Richtung, schade.



  • Jaa, also ich mache das immer so, dass ich Singletons fast nie benutze und globale Variablen (auch in unnamed namespaces) auch nicht.

    Mußt einen Uhrumlauf abwarten vor einem Fazit. Viele sind jetzt in der Schule oder arbeiten und dürfen gar nicht mitreden.



  • Und wenn man ein Objekt nur einmal braucht, sodass freie Funktionen sich anbieten würde, es aber etwas gibt, dass man kapseln möchte? Entweder nimmt man eine Singleton-Klasse oder eine globale Variable in einem unnamed namespace.



  • Eisflamme schrieb:

    Und wenn man ein Objekt nur einmal braucht, sodass freie Funktionen sich anbieten würde, es aber etwas gibt, dass man kapseln möchte? Entweder nimmt man eine Singleton-Klasse oder eine globale Variable in einem unnamed namespace.

    Ja, sehe ich auch so.

    Halt, manchmal erzeuge ich Einmal-Objekte auch in der main() und reiche sie zum Benutzer runter.
    Oder das klassische

    int main(){
       World w;
       return w.run();
    }
    


  • Und was benutzt Du dann von beiden Varianten lieber?

    Also verbietest Du Mehrfachinstanziierung von Einmal-Objekten nicht? Ich habe aber manchmal das Problem, dass ich Systemfunktionen aufrufe, die nicht mehrfach aufgerufen werden sollen, sodass Mehrfachinstanziierung also böse ist.



  • Eisflamme schrieb:

    Und was benutzt Du dann von beiden Varianten lieber?

    Das hängt ganz stark vom Problem ab, dass man hat. Es gibt ja gerade in C++ auch nicht die Singleton-Lösung, sondern diverse Geschmacksrichtungen, welche unterschiedliche Probleme lösen sollen.
    Insbesonderes zur Behandlung von Race Conditions bei der Singleton-Dekonstruktion gibt es ziemlich viele Ansätze, wobei Einer häßlicher als der Andere ist.
    Und je nachdem, was man für ein Singleton haben will, bietet es sich entweder an, dies über freie Funktionen oder über Klassen zu realisieren.



  • Eisflamme schrieb:

    Und was benutzt Du dann von beiden Varianten lieber?
    Also verbietest Du Mehrfachinstanziierung von Einmal-Objekten nicht?

    Normalerweise nicht. Im nächsten Programm will ich zwei Welten haben. Oder zwei Drucker.

    Eisflamme schrieb:

    Ich habe aber manchmal das Problem, dass ich Systemfunktionen aufrufe, die nicht mehrfach aufgerufen werden sollen, sodass Mehrfachinstanziierung also böse ist.

    Ja, dann muß ich es verbieten.
    Aber ich habe keine Faustregel, ob ich Singleton oder globale Variable im anonymen namesdpace bevorzuge. Wobei die beiden sogar dasselbe sind, wenn ich die freie ReadOnly-Zugriffsfunktion mitanbiete.
    Der wichtigste Unterschied ist wohl, ob ich eine static lokale Variable oder static globale Variable nehmen will. Ob ich dann

    CentralManagementUnit.AcquireVeryBigDonuts();
    

    oder

    CentralManagementUnitAcquireVeryBigDonuts();
    

    oder

    CentralManagementUnit::AcquireVeryBigDonuts();
    

    anbiete, hängt glaube ich von der aktuellen relativen Mondfeuchtigkeit ab. Da entwickelt sich bei mir kein absolute statement dazu, weil es so selten vorkommt, und ich jedes Jahr irgendwie anders drauf bin.
    Es gibt außerdem übliches Vorgehen: theApp ist global und nicht static in der main.cpp, theWorld lokal und nicht static in der main(), Hilfstabellen zum Speichermanager sind dort im anonymous namespace also global und static, der Systemdrucker ist ein Meyers-Singleton also lokal und static, die Winsock-DLL hat die Tendenz dazu, im anonymous namespace der Socket.cpp zu liegen also lokal und static, kann aber auch durchgereicht werden also lokal und nicht static.



  • Eisflamme schrieb:

    Und wenn man ein Objekt nur einmal braucht [...]

    Wenn man nur ein Objekt eines Typs braucht bedeutet das noch lange nicht dass das Singleton Pattern angebracht ist. Leider ist das nur wie die meisten das Singleton Pattern einsetzen. Das ist aber schlicht und einfach falsch. Der Zweck des Singleton Pattern ist es um jeden Preis sicherzustellen dass in keinem Fall niemals und überhaupt mehr als ein Objekt eines Typs erzeugt wird. Mit dem Singleton Pattern drückst du aus dass es eine Eigenschaft des Typs ist dass er nur einmal instanziert werden kann. Die Tatsache dass du beim Singleton üblicherweise einen globalen Zugriffspunkt auf die eine Instanz hast ist lediglich eine Folge aber nicht der Zweck des Singleton Pattern.



  • volkard schrieb:

    Eisflamme schrieb:

    Und was benutzt Du dann von beiden Varianten lieber?
    Also verbietest Du Mehrfachinstanziierung von Einmal-Objekten nicht?

    Normalerweise nicht. Im nächsten Programm will ich zwei Welten haben. Oder zwei Drucker.

    Muss sagen, das argument verstehe ich irgendwie nie so richtig. Normalerweise ist es einfacher ein Programm so zu schreiben, dass es von bestimmten Dingen nur eines gibt, weil man halt nur einen Fall prüfen muss. D.h. wenn ich mein Programm nur auf eine "Welt" auslege, kann ich eben nicht eine zweite einfach erstellen und das wird dann schon alles gutgehen. Entweder also programmierst du von Anfang an so das du x Welten erwartest, und machst dir falls du es nie braucht völlig unnötige Arbeit, oder aber du erlaubst es ein Objekt zu kopieren das logisch gesehen eben nicht kopiert werden darf.

    Ein Objekt das singleton ist mehrfach zu erlauben ist erstmal ne sehr einfach aufgabe.. Konstruktor public, getInstance oder so weg, dann variable global machen (oder aber auf dependency injection umstellen wenn man will). Aufrufe noch korrigieren und fertig. Dass das ganze mit einer dann tatsächlich zweiten Instanz des ehemaligen Singletons dann noch logisch funktioniert ist die interessantere und deutlich schwierigere frage. Wenn die beiden Welten nämlich tatsächlich vollständig unabhängig voneinander wären, könnte ich das programm ja einfach 2 mal starten. Da hat dot irgendwie recht, nur weil ich etwas nur einmal brauche ist es noch kein Grund für ein singleton.

    Vielleicht hab ich da eine etwas andere Philosophie, aber wenn ich erlaube das ein objekt kopiert wird/mehrfach erstellt wird dann will ich mir auch sicher sein dass es intuitiv und einwandfrei funktioniert. Auch wenn ich 3 Jahre später nachdem ich alles vergessen habe nochmal drüber schau. Aus demselben Grund mach ich auch operator= und copyctor private falls ich nach den aktuellen Anforderungen nicht klar entscheiden kann, was der eigentlich machen soll (und ich ihn noch eh nicht brauche).



  • kleiner Troll schrieb:

    Ein Objekt das singleton ist mehrfach zu erlauben ist erstmal ne sehr einfach aufgabe.. Konstruktor public, getInstance oder so weg, dann variable global machen (oder aber auf dependency injection umstellen wenn man will). Aufrufe noch korrigieren und fertig.

    Äh.
    Du hast sowas noch nie in einem grösseren Projekt gemacht, oder?
    Die Kleinigkeit "Aufrufe korrigieren" kann Tage dauern.



  • volkard

    theApp ist global und nicht static in der main.cpp, theWorld lokal und nicht static in der main(), Hilfstabellen zum Speichermanager sind dort im anonymous namespace also global und static, der Systemdrucker ist ein Meyers-Singleton also lokal und static, die Winsock-DLL hat die Tendenz dazu, im anonymous namespace der Socket.cpp zu liegen also lokal und static, kann aber auch durchgereicht werden also lokal und nicht static.

    Hmm... Wieso sind manche Dinge statisch und andere nicht? Global und lokal verstehe ich, das hat einfach mit den Zugriffsrechten zu tun bzw. gibt es für Kapselung halt lokal + Zugriffsmethode.

    Oder meinst Du ein anderes static als das mit dem static -Schlüsselwort? Und ich nehme an, mit global + nicht-static meinst Du nicht, dass das innerhalb der main()-Funktion deklariert ist, oder? Falls doch, inwiefern ist das denn global?

    dot:
    Die Punkte sind mir schon bewusst, danke. 🙂



  • Naja, was ist für dich "größer"?
    200k+ Zeilen? Nö hab ich zugegebenermaßen nicht.
    20k+ Zeilen? Jop.

    Sicher ist das nicht ne Sache von 5 Minuten, zumindest je nachdem wie tief das Singleton im Programm verankert ist.. Aber da es prinzipiell eine einfache Aufgabe ist muss man sich nicht so viele Sorgen um bugs die man einführen würde während dieser arbeit machen.

    "Tage" dauern würd ich für übertrieben halten,zumindest wenn man die variante mit der globalen variable nimmt.
    Kann man ja einfach nach namespace::GetInstance() suchen und das entsprechend ersetzen. Logisch muss man am code ja überhaupt nichts ändern.

    Schwieriger wirds natürlich wenn man auf dependency injection umstellt. Aber auch das seh ich als machbar an - vorallem als viel weniger arbeit als ein Riesenprogramm auf spezifikationen auszulegen, dies in 80% der Fälle nie brauchen wird.



  • kleiner Troll schrieb:

    volkard schrieb:

    Eisflamme schrieb:

    Und was benutzt Du dann von beiden Varianten lieber?
    Also verbietest Du Mehrfachinstanziierung von Einmal-Objekten nicht?

    Normalerweise nicht. Im nächsten Programm will ich zwei Welten haben. Oder zwei Drucker.

    Muss sagen, das argument verstehe ich irgendwie nie so richtig. Normalerweise ist es einfacher ein Programm so zu schreiben, dass es von bestimmten Dingen nur eines gibt, weil man halt nur einen Fall prüfen muss. D.h. wenn ich mein Programm nur auf eine "Welt" auslege, kann ich eben nicht eine zweite einfach erstellen und das wird dann schon alles gutgehen. Entweder also programmierst du von Anfang an so das du x Welten erwartest, und machst dir falls du es nie braucht völlig unnötige Arbeit, oder aber du erlaubst es ein Objekt zu kopieren das logisch gesehen eben nicht kopiert werden darf.

    Tut mir leid, aber ich programmiere zuerst ein Game mit nur einer Welt. Und denke dabei absolut nicht, daß mehrere Welten möglich wären. Und dann wenn ich es zum Multi-Player-Game ausbauen will, wäre ich verratzt, wenn ich die Welt-Klasse (ohne Not!) zum Singleton gemacht hätte.



  • kleiner Troll schrieb:

    Ein Objekt das singleton ist mehrfach zu erlauben ist erstmal ne sehr einfach aufgabe.. Konstruktor public, getInstance oder so weg, dann variable global machen (oder aber auf dependency injection umstellen wenn man will). Aufrufe noch korrigieren und fertig.

    Falsch.
    Weil es ein Singleton war, haben die Armeen und Häuser, die Dialoge und Buttons NICHT gespeichert, auf welche Welt sie wirken, denn es gibt ja nur eine.
    Gibt es auf einmal zwei Welten, muß alles neu angefaßt werden.
    Das Singleton-Versprechen ist so stark, das tut man nicht ohne Not mal dahinsagen. Jedenfalls ich nicht.

    kleiner Troll schrieb:

    Aus demselben Grund mach ich auch operator= und copyctor private falls ich nach den aktuellen Anforderungen nicht klar entscheiden kann, was der eigentlich machen soll (und ich ihn noch eh nicht brauche).

    So ich auch.



  • Eisflamme schrieb:

    Hmm... Wieso sind manche Dinge statisch und andere nicht?

    Bei den globalen: Aus Tradition, nur aus Tradition, ohne, daß das besonders sinnvoll wäre.



  • Vollkommen falsch! Falsch. Falsch. Falsch. Falsch. Falsch.

    Dazu zitiere ich mich einfach mal selbst

    Entweder also programmierst du von Anfang an so das du x Welten erwartest, und machst dir falls du es nie braucht völlig unnötige Arbeit, oder aber du erlaubst es ein Objekt zu kopieren das logisch gesehen eben nicht kopiert werden darf.

    Der Text ging eben unter der Annahme, dass das restliche Programm bereits mit mehreren instanzen umgehen kann.

    Du bist also der Meinung du schreibst stunden um stunden code um mehrere Welten zu unterstützen die du vielleicht mal brauchen KÖNNTEST? Bitte, verbiete ich dir nicht. Ich spar mir diese Arbeit - und mach konsequenterweise dann eben die Instanz, dies nur einmal geben darf zu einem singleton. Und falls sich doch tatsächlich die Anforderung ändert, kann ich diese Änderungen nachliefern. Das es syntaktisch bis dahin ein singleton war stört mich nicht weiter, weil eben nur das umzustellen ist ein moderator wenn auch zugegebenermaßen relevanter aufwand. Der logische/Design- Aufwand ist deutlich größer - egal ob man das jetzt von Anfang an macht oder nachholt.
    Nur häufig baut man sich halt nunmal Flexibilität mit viel mühen die man niemals braucht. Man kann sich natürlich wunderbar streiten was mehr arbeit macht, nachrüsten oder alles "richtig" machen damit man, falls man später etwas tatsächlich braucht weniger arbeit hat. Ist auch eine Frage wie wahrscheinlich es ist das sich entsprechende Anforderungen ändern. Muss man abschätzen, und wenn ich der Meinung bin es ist unwahrscheinlich das ichs später brauche mach ich ein singleton draus, inklusive vereinfachter logik. Deswegen überzeugt mich dieses Gebetsmühlenartige "aber man könnte es später ja mehrmals brauchen" auch nicht. Diese Aussage ist zwar imho völlig korrekt, aber leider nicht das einzige, was es zu beachten gibt.

    Und btw, auch im multiplayer gibt's nur eine Welt. Es gibt mehrere Spieler, aber (vom Modell her) immer noch eine Welt. Oder spielt dein Multiplayer in zwei verschiedenen leveln? (tatsächlich hat natürlich jeder seine eigene Kopie, und um das zu synchronisieren gibt's halt oft einen server der DIE EINE Welt hält und entscheidet was jetzt wie ist - die anderen haben eher nur ein Abbild)



  • Nur falls sich jemand wundert, Volkard hat nachdem ich zitiert habe seinen Text noch leicht geändert. (darfst gerne meinen post oben korrigieren und den hier löschen, inhaltlich ändert sich ja nichts)


Anmelden zum Antworten