Singleton

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Singleton

Beitragvon str1442 » Freitag 21. November 2008, 21:54

Gesetzt folgender Fall:

Ich habe drei Klassen A, B und C. Jede davon nimmt eine Aufgabe komplett wahr und braucht nur noch genutzt zu werden. Keine dieser Klassen darf mehr als eine Instanz besitzen.

Konkret wäre ein Beispiel für sowas eine Art "FileSystem" Klasse, die intern alle eingelesen Pfade / Filenames speichert und damit umzugehen weiß. Natürlich darf es keine zwei FileSystem Instanzen geben, schließlich gibts auch nur ein FileSystem.

Klasse B und C brauchen nun beide die Instanz der Klasse A, die wiederrum selbst zentral von einer Art "Main" Modul instanziert wurde.

Mein Gedankengang bzw meine Idee zur Lösung:

Ich mache Klasse A zu einem Singleton. B und C importieren das Modul, in dem die Klasse definiert wurde, und versuchen sie zu inittieren. Dadurch bekommen beide die Instanz, die vorher vom Main Modul angelegt wurde. Funktionieren sollte das, da ein Modul ja nicht mehrmal importiert, sondern nach dem ersten Import in sys.modules angefragt wird.

--> B und C können A ohne weitere Probleme nutzen.

Frage: Ist das sauber / gut / was auch immer?

Vorteile sehe ich darin, das diese Klassen sowieso nur einmal instanziert werden können. Ich habe einen definierten Einstiegpunkt (Die Klasse selbst).
Es ist auch gut nachvollziehbar. Nach dem Starten des Programmes wird die Instanz genau einmal instanziert, im Main Modul. Wird sie danach wieder benutzt, brauche ich mich um nichts zu kümmern.

Die Kapselung bleibt gewahrt. Statt im Main Modul mit einer globalen Variable zu hantieren, bleibt die Instanz und Logik bei der Klasse.

Würde ich es nicht so umsetzen, hätte ich eine riesige Anlaufstelle im "Main Modul". Das bringt in "niederen" Hierarchien automatisch Probleme mit, da Main neben dem reinem Initiieren auch eine Art "Mainloop" beinhalten müsste, und ich alles, was passiert, bis in die unteren Hierarchien weitergeben müsste.

Nachteile:
Das ist eine Art global, ja. Aber: Solang ich nur "Read-Only" auf solche Systeme zugreife (was ja der Sinn und Zweck ist), sollte es keine Probleme geben.
Benutzeravatar
Leonidas
Administrator
Beiträge: 16023
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Re: Singleton

Beitragvon Leonidas » Samstag 22. November 2008, 02:18

str1442 hat geschrieben:Konkret wäre ein Beispiel für sowas eine Art "FileSystem" Klasse, die intern alle eingelesen Pfade / Filenames speichert und damit umzugehen weiß. Natürlich darf es keine zwei FileSystem Instanzen geben, schließlich gibts auch nur ein FileSystem.

Also bei mir gibt es ganz viele. Jedes mal wenn ich eine CD einlege kommt ein weiteres dazu, wenn ich einen USB-Stick anstecke kommt sogar noch ein anderes. Und wenn ich statt der CD eine DVD reinstecke, dann gehts erst recht rund.

Singleon considered stupid, Singleton I love you, but you're bringing me down und Patterns I Hate #1: Singleton oder auch The Python Singleton Unpattern (man sieht, in letzter Zeit hat das Singleton-Pattern ziemlichen Gegenwind abbekommen). Letztendlich sind Singletons im großen und ganzen die objektorientierte Version von globalen Variablen.

Statt einer Filesystem-Klasse würde ich eher den Dateisystemzugriff locken und mehrere Filesystem-Klassen zulassen.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
BlackJack

Beitragvon BlackJack » Samstag 22. November 2008, 09:59

@Leonidas: Also ich habe nur ein "Dateisystem". Wenn ich einen USB-Stick einstecke oder eine CD/DVD einlege, dann wird das *eine* "Dateisystem" an entsprechender Stelle nur erweitert.

Eine recht schöne Singleton-Lösung ist IMHO auch einfach nur ein Exemplar von einer Klasse zu erstellen. Man braucht nicht immer Mechanismen die erzwingen, was man auch freiwillig machen kann. Wir sind hier ja nicht bei Java. :-)
Benutzeravatar
Leonidas
Administrator
Beiträge: 16023
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Beitragvon Leonidas » Samstag 22. November 2008, 10:57

BlackJack hat geschrieben:@Leonidas: Also ich habe nur ein "Dateisystem". Wenn ich einen USB-Stick einstecke oder eine CD/DVD einlege, dann wird das *eine* "Dateisystem" an entsprechender Stelle nur erweitert.

``mount`` zeigt mir mehrere Teile die verschiedene Dateisysteme haben. Die mtools etwa greifen auf nicht gemountete DOS-Dateisysteme zu.. Was würdest du dann unter Windows sagen, wo es eigentlich keinen gemeinsamen Wurzelknoten gibt? Aber vermutlich artet das eh wieder in eine Diskussion um Definitionen aus - für den Thread eher nicht relevant.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
BlackJack

Beitragvon BlackJack » Samstag 22. November 2008, 12:11

Es ging ja nicht um die einzelnen konkreten Dateisysteme auf den Datenträgern, sondern um die Schnittstelle für Benutzer und Progeamme, und die präsentiert sich als ein grosser Verzeichnisbaum. Jedenfalls in vernünftigen Betriebssystemen. ;-)
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

Beitragvon str1442 » Samstag 22. November 2008, 19:55

Es ging ja nicht um die einzelnen konkreten Dateisysteme auf den Datenträgern, sondern um die Schnittstelle für Benutzer und Progeamme, und die präsentiert sich als ein grosser Verzeichnisbaum. Jedenfalls in vernünftigen Betriebssystemen.


Genau das meinte ich. Und man muss bei zb Windows nun auch nicht derartige Ausnahmen machen, sondern kann C: D: etc auch als Ordner unter einer einheitlichen Wurzel darstellen lassen, kommt rein programmtechnisch aufs selbe raus.

@Singleton:

Danke für die Links. Grade die Geschichte mit dem Unittesting hatte ich überhaupt nicht bedacht.

Grundlegend ging es auch eher weniger darum, nur eine einzige Instanz zuzulassen, sondern eher, mittels der Klasse dieser Instanz einen (ja, ich gebs zu) globalen Zugriffspunkt einführen zu können. Ich weiß / wusste nicht, wie ich am besten derartige "Subsysteme" (wie zb "FileSystem") am besten mit anderen verbinden könnte.

Dieses Zitat hier:

Of course I loved all of Design Patterns, except for pages 243 to 256, which had the magic property of inducing a coma-like trance whenever I tried to skim through them.


Hat mich auf den angegebenen Seiten mal nachschauen lassen, wo ich mitunter auf Proxy und "Chain of Responsibility" (und die Besprechung der Struktural Patterns) stieß. Grade Chain of Responsibility scheint ein vernünftiger Ansatz zu sein, Kommunikation von unten nach Oben weiterleiten zu lassen.

Einer der Links leitete mich dann auch weiter zu einem Blog Eintrag, in dem gesagt wird, das man die Erzeugung von Objekten (mehr oder weniger) strikt von ihrem Einsatz trennen soll, sofern es keine "Primitives" (int usw) sind oder es einen guten Grund gibt. (Das verletze, würde man es tun, außerdem das "Single Responsibility Prinzip" - gut, könnte man sagen)

Insofern werde ich wohl sehen, das ich mittels verschiedener "Factory" Klassen und dem "Facade" Pattern (was ich implizit sowieso angewendet hatte) eine Art Nachrichtensystem zwischen den verschiedenen Subsystemen baue und die Erzeugung von Objekten komplett des Factory / Builder Objekten überlasse. Wie genau, kann man (oder zumindest ich) sowieso nicht aus dem Kopf heraus erzählen, aber irgendwie so sollte es funktionieren ^_^.

Wobei mir grade einfällt, das man im Grunde auch statt einer Instanz jedes Feld eines Objektes zu einer Klassenvariable machen könnte, und jede neue Instanz dann sowieso die gleichen Attribute hätte, aber egal.

Für alle zukünftigen Leser, die nach "Singleton" gesucht haben, und jetzt immernoch ein Singleton haben möchten, dieser Code:

Code: Alles auswählen

class SingletonMetaClass(type):
    singletons = {}

    def __call__(self, *args, **kwargs):
        # __call__ is called when an object is called via ().
        # The object will use __call__ from its class and give itself to it.
        # We use this to create Singletons: When the class (instance of this
        # metaclass) is called (to create a new object), this __call__
        # method is about to be called.

        if self not in self.singletons:
            self.singletons[self] = type.__call__(self, *args, **kwargs)
        # If we havent already created the instance, create it now by calling
        # type's __call__ (which will call type.__new__ and the newly objects
        # __init__). Otherwise, return the saved instance in the singleton
        # dict.
        # Effect: Beyond suppressing the call of the classes __new__, we
        # also take care that the new objects __init__ is not called.

        return self.singletons[self]

class Singleton(object):
    __metaclass__ = SingletonMetaClass


Eine etwas andere Form davon habe ich im Netz gefunden, welche ich dann adaptiert habe. Ist ein wenig "Magic", aber funktioniert.
Mad-Marty
User
Beiträge: 317
Registriert: Mittwoch 18. Januar 2006, 19:46

Beitragvon Mad-Marty » Freitag 28. November 2008, 16:52

Bei aller Begeisterung für DPs ... immer dran denken das der Klassiker
a) für statische Sprachen war
b) dicht an C++ angelehnt


Wie schon erwähnt ist das Singleton nicht immer das wahre, Borg pattern ist u.U. eine gute alternative.
Benutzeravatar
Leonidas
Administrator
Beiträge: 16023
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Beitragvon Leonidas » Freitag 28. November 2008, 17:56

Mad-Marty hat geschrieben:Wie schon erwähnt ist das Singleton nicht immer das wahre, Borg pattern ist u.U. eine gute alternative.

Das Borg Pattern ist effektiv das Singleton, nur eben anders Implementiert. Die Links die ich gepostet habe kritisieren das Singleton-Pattern weil es sich so verhält wie es sich verhält, insofern trifft die Kritik 1:1 auch auf das Borg-Pattern zu.
My god, it's full of CARs! | Leonidasvoice vs Modvoice

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder