Singleton kuerzer?



  • Hi,

    ich bin dabei einen Zufallszahlengenerator zu schreiben (mit recht speziellen Anforderungen) und habe versucht, das Ganze mittels Singleton umzusetzen:

    class RandomNumberGenerator
    {
    	public:
    		static unsigned get_counter() {return get_instance()->_get_counter();}
    		static double uniform_01() {return get_instance()->_uniform_01();}
    	private:
    		static boost::shared_ptr<RandomNumberGenerator> get_instance();
    		static unsigned get_random_number() {return get_instance()->_get_random_number();}
    		static unsigned num_random_number() {return get_instance()->_num_random_number();}
    
    		RandomNumberGenerator();
    		unsigned _get_random_number() {return random_numbers[counter++];}
    		unsigned _num_random_number() const {return random_numbers.size();}
    		unsigned _get_counter() const {return counter;}
    		double _uniform_01();
    
    		static boost::shared_ptr<RandomNumberGenerator> instance;
    
    		int counter;
    		std::vector<int> random_numbers;
    };
    

    Zwar erreiche ich so mein Ziel, mir keine weiteren Gedanken ueber die korrekte Initialisierung zu machen, ich frage mich allerdings, ob das Duplizieren der Methoden, also

    static unsigned get_counter();
    		static double uniform_01();
    		static unsigned get_random_number();
    		static unsigned num_random_number();
    

    bzw.

    unsigned _get_random_number();
    		unsigned _num_random_number();
    		unsigned _get_counter() const;
    		double _uniform_01();
    

    irgendwie eleganter geht. Faellt jemanden dazu was ein?



  • Wieso überhaupt ein Singleton?



  • Na klar geht das kürzer:

    class RandomNumberGenerator
    {
    	public:
    		static unsigned int get_counter() {return get_instance()->counter;}
    		static boost::shared_ptr<RandomNumberGenerator> get_instance() { return instance;}
    	private:
    		static boost::shared_ptr<RandomNumberGenerator> instance;
    		unsigned int counter;
    };
    

    Mal auszugsweise, den Rest solltest du selbst anpassen können.
    Wie kamst du eigentlich auf die Idee, die Methoden zu duplizieren? Ist doch überhaupt nicht notwendig.



  • mad_martin schrieb:

    Wie kamst du eigentlich auf die Idee, die Methoden zu duplizieren? Ist doch überhaupt nicht notwendig.

    2 Gruende:

    • zu wenig Schlaf

    • ich wollte folgendes trennen:

    • den Teil vom Singleton, den jede Klasse RandomNumberGenerator haben wuerde, wenn ich mich irgendwann entscheiden sollte, dass es doch mehr als nur eine Instanz von RandomNumberGenerator geben soll

    • den Teil, der nur vorhanden ist, wenn man RandomNumberGenerator als Singleton verwendet (und nicht, wenn man irgendwann mehrere Instanzen haben sollte)



  • Falls man von der Klasse mehrere Objekte erstellen will, hat diese nichts mehr mit einer Singleton Klasse zu tun. Man sollte vorher überlegen ob es Sinn ergibt ein Singleton darauszumachen. Eine halbe Singletonklasse es nämich das Sinnfreie daran. Sowieso sieht dein Singleton, meiner Meinung nach, ziemlich merkwürdig aus und wenn du jetzt schon mit dem Gedanken spielst, dass du mehrere Objekte verwenden wirst, lass das Singleton weg. Wenn du nur ein Objekt brauchst, dann erstellst du halt nur eins.



  • Singletons sind sowieso sinnfrei. Ganz speziell in diesem Fall. Aber auch sonst.



  • hustbaer schrieb:

    Singletons sind sowieso sinnfrei. Ganz speziell in diesem Fall. Aber auch sonst.

    Das ist ebenfalls sinnfrei...



  • Ja, vermutlich.



  • ingobulla schrieb:

    2 Gruende:

    • zu wenig Schlaf

    • ich wollte folgendes trennen:

    • den Teil vom Singleton, den jede Klasse RandomNumberGenerator haben wuerde, wenn ich mich irgendwann entscheiden sollte, dass es doch mehr als nur eine Instanz von RandomNumberGenerator geben soll

    • den Teil, der nur vorhanden ist, wenn man RandomNumberGenerator als Singleton verwendet (und nicht, wenn man irgendwann mehrere Instanzen haben sollte)

    Gut, Grund 1 ist bekannt als Verursacher merkwürdiger Konstruktionen.
    Aber Grund 2 ist irgendwie fragwürdig. Wenn Singleton, dann richtig. Oder halt keins.

    hustbaer schrieb:

    Singletons sind sowieso sinnfrei. Ganz speziell in diesem Fall. Aber auch sonst.

    Meinst du wirklich? Warum?



  • mad_martin schrieb:

    Meinst du wirklich? Warum?

    Ich würde Singletons nicht unbedingt als sinnfrei, das Singleton-Pattern aber auf jeden Fall als gefährlich bezeichnen - besonders im Hinblick auf Lazy Initialisation und Multithreading.

    Persönlich lasse ich mir Singletons daher, wo nötig, vom Framework bereitstellen (in Java Spring, in C++ pococapsule).



  • Kessi-MC schrieb:

    besonders im Hinblick auf Lazy Initialisation und Multithreading.

    Erklär mal.
    Ich gehe erstmal vom Meyers-Singleton aus.

    Lazy Initialisation (oder auf englisch lazy initialization) ist doch keine Gefahr, sondern ein kleiner Bonus. Egal, wie spät die Initialisierung stattfindet, es ist immer noch rechtzeitig. Ich sehe da keine Gefahr.

    Multithreading ist doch keine Gefahr. Der Compiler schützt die Initialisierung von static-Variablen schon. http://stackoverflow.com/questions/1270927/are-function-static-variables-thread-safe-in-gcc

    Kessi-MC schrieb:

    Persönlich lasse ich mir Singletons daher, wo nötig, vom Framework bereitstellen (in Java Spring, in C++ pococapsule).

    Brilliant.



  • volkard schrieb:

    Multithreading ist doch keine Gefahr. Der Compiler schützt die Initialisierung von static-Variablen schon. http://stackoverflow.com/questions/1270927/are-function-static-variables-thread-safe-in-gcc

    Das wäre wirklich schön *seufz*.

    Bloß trifft es nach dem, was man unter deinem Link findet, nur auf GCC zu. Jemand sagt dort explizit, dass MSVC es nicht so macht. Die Lösung ist also bestenfalls uneinheitlich, was ich nachvollziehbar finde, da der Standard (bisher) Threads ignoriert. Ein ernsthafter Singleton-Bauer muss also wohl davon ausgehen, dass die Initialisierung lokaler statischer Variablen nicht Thread-Save ist.

    Trotzdem sollte man das Pattern natürlich nicht pauschal verdammen. Es kann unter Umständen schon nützlich sein.

    Stefan.



  • Jemand sagt dort explizit, dass MSVC es nicht so macht.

    Das kann ich persönlich bestätigen 🙂

    Und damit ist (Korrektur: wäre) auch bloss das Thema Initialisierung erledigt. Das Cleanup-Problem löst sich dadurch nicht.



  • hustbaer schrieb:

    Und damit ist (Korrektur: wäre) auch bloss das Thema Initialisierung erledigt. Das Cleanup-Problem löst sich dadurch nicht.

    Bitte nicht hauen 😉 aber ich nehme das nicht allzu ernst. Klar, wenn man sich theoretisch mit Singletons beschäftigt oder eine allgemeine Klasse schreiben möchte, sollte man sich schon mit Initialisierung und Cleanup auseinandersetzen. Sei es, um Kunden der Klasse über die Einsatz-Bedingungen aufzuklären.

    Andererseits aber funktionieren konkrete Singletons meiner Erfahrung nach sehr gut. Es gibt oft gar keinen Grund, sich um die (theoretischen) Schwierigkeiten zu kümmern.

    Und außerdem: Was wäre die Alternative? Offene globale Variablen sicher nicht. Das Herumreichen von Objekten meist auch nicht. Insgesamt meine ich, dass man die Gefahren des Patterns schon kennen sollte - diese aber andererseits bei konkreten Anwendungsfällen gar nicht ins Gewicht fallen. Und bevor man sich mit den Alternativen einen abbricht, nimmt man halt ein Singleton.

    Stefan.



  • Warum ist ein Singleton globalen Funktionen + Namespace überlegen? Warum nicht einfach:

    namespace RandomNumberGenerator
    {
        unsigned get_counter();
        double uniform_01();
    }
    


  • Ben04 schrieb:

    Warum ist ein Singleton globalen Funktionen + Namespace überlegen? Warum nicht einfach:

    namespace RandomNumberGenerator
    {
        unsigned get_counter();
        double uniform_01();
    }
    

    Weil hinter einem Singleton eine Klasse steckt, die Member kapselt. Somit muss nicht irgendwo ein globales Objekt runliegen, wenn du mit mehreren Methoden darauf zugreifen willst.
    Z.B....



  • Ben04 schrieb:

    Warum ist ein Singleton globalen Funktionen + Namespace überlegen? Warum nicht einfach:

    namespace RandomNumberGenerator
    {
        unsigned get_counter();
        double uniform_01();
    }
    

    Lokale Zufallszahlengeneratoren können Vorteile haben. Deswegen mag man "class RandomNumberGenerator". Meistens braucht man aber dann doch nur einen globalen und den macht man zum Singleton, damit das Kind keinen so häßlichen Namen hat.



  • Joladrio schrieb:

    Ben04 schrieb:

    Warum ist ein Singleton globalen Funktionen + Namespace überlegen? Warum nicht einfach:

    namespace RandomNumberGenerator
    {
        unsigned get_counter();
        double uniform_01();
    }
    

    Weil hinter einem Singleton eine Klasse steckt, die Member kapselt. Somit muss nicht irgendwo ein globales Objekt runliegen, wenn du mit mehreren Methoden darauf zugreifen willst.
    Z.B....

    namespace RandomNumberGenerator
    {
        unsigned get_counter();
        double uniform_01();
    }
    // ...
    namespace RandomNumberGenerator
    {
        static int my_data = 0;
        unsigned get_counter(){
             ...
        }
    }
    

    Welche globale Variable?

    volkard schrieb:

    Ben04 schrieb:

    Warum ist ein Singleton globalen Funktionen + Namespace überlegen? Warum nicht einfach:

    namespace RandomNumberGenerator
    {
        unsigned get_counter();
        double uniform_01();
    }
    

    Lokale Zufallszahlengeneratoren können Vorteile haben. Deswegen mag man "class RandomNumberGenerator". Meistens braucht man aber dann doch nur einen globalen und den macht man zum Singleton, damit das Kind keinen so häßlichen Namen hat.

    Du willst also kein Singleton sondern eine globale Instanz deiner Klasse. Lokal instantiieren kann man das Singleton aus dem ersten Post jedenfalls nicht.



  • Ben04 schrieb:

    Du willst also kein Singleton sondern eine globale Instanz deiner Klasse.

    Im Prinzip schon. Wobei das späte Initialisieren zum Beispiel beim Mersenne Prime Twin Generator auch ganz lecker ist. Ich will aber keinen Zwang, daß es nur eine Instanz geben kann.

    Ben04 schrieb:

    Lokal instantiieren kann man das Singleton aus dem ersten Post jedenfalls nicht.

    Jetzt in ich ein trauriger Programmierbär.



  • Ben04 schrieb:

    namespace RandomNumberGenerator
    {
        unsigned get_counter();
        double uniform_01();
    }
    // ...
    namespace RandomNumberGenerator
    {
        static int my_data = 0;
        unsigned get_counter(){
             ...
        }
    }
    

    Welche globale Variable?

    OK, global ist sie nicht, aber eben nicht gekapselt.
    Ich kann von außen RandomNumberGenerator::my_data verändern, ohne dass die Methoden etwas davon mibekommen, und das kann ziemlich Mist erzeugen. Im Falle eines Zufallszahlengenerators z.B. eine nicht reproduzierbare Änderung in der Zahlenfolge.
    Nicht nett, dass du das eigentliche Argument, nämlich die Kapselung durch die Klasse, einfach unterschlägst und auf einem falsch verwendeten Begriff rumreitest. 😞


Anmelden zum Antworten