Singletons vermeiden



  • Hallo!

    Angenommen ihr programmiert eine Software in C++, welche gewisse Hardwarekomponenten in Form von Klassen repräsentiert.
    Beispielsweise könnte man sich da ein GPRS Modem vorstellen, welches über eine serielle Schnittstelle mit der SW kommuniziert.

    Natürlich darf diese Klasse nur einmal instanziert werden, da es zu undefiniertem Verhalten führen würde, wenn 5 Instanzen gleichzeitig mit der Hardware kommunizieren würden.

    Die naheliegendste Lösung ist nun die Verwendung des Singleton Patterns.
    Inzwischen wimmelt es in der SW aber nur so von Singletons, und so wirklich gefallen tut mir das nicht.

    Gibt es eine elegantere Möglichkeit, um das Problem ohne den genannten Pattern zu lösen?

    P.S.: Das Problem tritt ja nicht nur bei Hardware auf, sondern auch bei diversen anderen Ressourcen. Beispielsweise gibt es eine Klasse, die auf einem definierten Port lauscht und bei Bedarf eine Aktion ausführt, und danach antwortet. Auch hier darf natürlich nur ein Objekt erstellt werden und sich vom OS den Port geben lassen. Wieder eine Situation, wo ein Singleton das Problem löst. Muss das sein...?



  • Ein Singleton ist - wenn überhaupt - nur in so weit eine Lösung, als es andere Objekte in der gleichen Applikation betrifft. Du kannst nicht verhindern, dass eine zweite Applikation versucht auf die selbe Hardware zuzugreifen.
    Im Allgemeinen bietet ein OS die Möglichkeit eine Resource exklusiv zu öffnen, eine zweites Öffnen würde dann zu einem Fehler führen. Diesen Weg halte ich in diesem Fall für angemessen.

    siehe zum Beispiel CreateFile bei Windows; 3. Parameter dwShareMode=0 .

    Gruß
    Werner



  • Ich versteh gar nicht, was alle gegen Singletons haben.
    Die Alternative wären freie Funktionen, wo die globalen Daten dann irgendwo in der cpp im einem anonymen namespace liegen. Und das kann zu Problemen mit der Initialisierung/Zerstörung führen. Bei Singletons hat man da mehr Kontrolle.
    Wenn du wirklich etwas repräsentierst, was logisch gesehen nur eine Instanz haben kann, dann nimm ruhig ein Singleton.
    Mit aller Kraft etwas zu vermeiden, nur weil Singleton, führt nicht unbedingt zu besseren Code.



  • Der Fehler ist, dass du denkst, es könne nur eine Instanz geben. Es kann durchaus mehrere geben. Die Klasse für das GPRS-Modem wird für jedes einzelne Modem instanziiert, entsprechende Hardwareadressen werden als Konstruktorparameter übergeben. Genauso hast du eine Instanz pro Port. Wenn du zufälligerweise nur ein Modem und einen Port hast, dann hast du halt nur jeweils eine Instanz, aber kein Singleton. Du kannst einen Laufzeitfehler schmeißen, wenn jemand versucht eine Instanz für eine bereits benutzte Ressource anzulegen. Natürlich hast du dann den Nachteil, dass du nicht mehr "Das GPRS-Modem" ansprichst, sondern "Ein GPRS-Modem", und vielleicht nützt dir die gewonnene Flexibilität in Ermangelung von Modems nichts, aber für die Vermeidung der globalen Variablen (auch Singleton genannt) ist es das vielleicht wert.



  • Vielleicht stellt sich auch raus, dass die Klasse, die mit dem GPRS Modem viele Ressourcen braucht. Dann könnte man sie einmal erstellen, dann löschen (weil sie im Standby z.B. nicht gebraucht wird) und dann neu erstellen.

    Es gibt viele Gründe, wieso etwas kein Singleton sein sollte. Nur weil im Moment keiner zutrifft, heisst das nicht, dass es immer so sein wird.



  • danke für eure einschätzungen...

    ...ok gut, das sehe ich ein, dass evtl. die gleiche klassenlogik auch noch auf einem anderen port laufen soll.

    in dem fall also kein singleton, sondern über den CTOR den port mitübergeben.
    falls der port bereits belegt ist, eine exception werfen.
    aber zumindest Copy-CTOR und operator= sollten dann trotzdem private sein, weil sonst zwei instanzen auf den gleichen port zugreifen möchten.



  • Nicht kopierbar (private, besser mit delete), aber dafür können die Resourcen verschoben werden, heißt move-constructor existiert ggf.



  • Marthog schrieb:

    Nicht kopierbar (private, besser mit delete), aber dafür können die Resourcen verschoben werden, heißt move-constructor existiert.

    achso, ich sollte anmerken dass unser compiler leider kein c++11 kann, also auch kein move.



  • Nathan schrieb:

    Ich versteh gar nicht, was alle gegen Singletons haben.
    (...blah...)

    Du hast nichtmal den ganzen Beitrag von "vermeider" gelesen, oder? Ich meine, wieso sollte es nur ein GPRS Modem geben?



  • vermeider schrieb:

    aber zumindest Copy-CTOR und operator= sollten dann trotzdem private sein, weil sonst zwei instanzen auf den gleichen port zugreifen möchten.

    Ja, klar.
    Die Meisten Klassen die Resourcen repräsentieren sind prinzipiell non-copyable, und sollten daher auch keinen Copy-Ctor/Assignment-Operator haben.


Anmelden zum Antworten