Singleton (Entwurfsmuster)
Das Singleton (selten auch Einzelstück genannt) ist ein in der Softwareentwicklung eingesetztes Entwurfsmuster und gehört zur Kategorie der Erzeugungsmuster (engl. creational patterns). Es stellt sicher, dass von einer Klasse genau ein Objekt existiert.[1] Dieses Singleton ist darüber hinaus üblicherweise global verfügbar. Das Muster ist eines der von der sogenannten Viererbande (GoF) publizierten Muster.
Verwendung
[Bearbeiten | Quelltext bearbeiten]Das Singleton findet Verwendung, wenn
- nur ein Objekt zu einer Klasse existieren darf und ein einfacher Zugriff auf dieses Objekt benötigt wird oder
- das einzige Objekt durch Unterklassenbildung spezialisiert werden soll.
Anwendungsbeispiele sind
- ein zentrales Protokoll-Objekt, das Ausgaben in eine Datei schreibt.
- Druckaufträge, die zu einem Drucker gesendet werden, sollen nur in einen einzigen Puffer geschrieben werden.
UML-Diagramm
[Bearbeiten | Quelltext bearbeiten]Eigenschaften
[Bearbeiten | Quelltext bearbeiten]Das Einzelstück (Singleton)
- erzeugt und verwaltet das einzige Objekt der Klasse
- bietet globalen Zugriff auf dieses Objekt über eine Instanzoperation (getInstance()).
Dabei ist
- die Instanzoperation eine Klassenmethode, das heißt statisch gebunden
- das private Attribut „Instanz“ (singleton) ein Klassenattribut, das heißt ein statisches Attribut.
In Klammern stehen die Bezeichnungen aus obiger Abbildung.
Vorteile
[Bearbeiten | Quelltext bearbeiten]Das Muster bietet eine Verbesserung gegenüber globalen Variablen:
- Zugriffskontrolle kann realisiert werden.
- Das Singleton kann durch Unterklassenbildung spezialisiert werden.
- Welche Unterklasse verwendet werden soll, kann zur Laufzeit entschieden werden.
- Die Einzelinstanz muss nur erzeugt werden, wenn sie benötigt wird.
- Sollten später mehrere Objekte benötigt werden, ist eine Änderung leichter möglich als bei globalen Variablen.
Nachteile
[Bearbeiten | Quelltext bearbeiten]- Es besteht die große Gefahr, durch exzessive Verwendung von Singletons quasi ein Äquivalent zu globalen Variablen zu implementieren und damit dann prozedural anstatt objektorientiert zu programmieren.[2]
- Abhängigkeiten zur Singleton-Klasse werden verschleiert, d. h. ob eine Singleton-Klasse verwendet wird, erschließt sich nicht aus dem Interface einer Klasse, sondern nur anhand der Implementierung.[3] Zudem wird die Kopplung erhöht, was Wiederverwendbarkeit und Übersichtlichkeit einschränkt.
- Der „Scope“ eines Singletons, also der Bereich, in dem ein Singleton auch wirklich technisch „einzeln“ ist, muss nicht mit dem Bereich zusammenfallen, in dem es „einzeln“ sein soll. Konkret ist bei nebenläufigen oder gar verteilten Systemen schwierig sicherzustellen, dass wirklich nur eine Instanz existiert.
- Beispielsweise ist in Java eine einfache static-Variable „einzeln je ClassLoader“; in verteilten (zum Beispiel cluster-fähigen) Systemen oder komplexen nebenläufigen Applikationen wie Application-Servern können dadurch wieder mehrere Instanzen nebeneinander existieren.
- In Systemen mit parallelen Abläufen (Threads) muss sichergestellt sein, dass nicht durch parallele Initialisierung kurzfristig mehr als eine Instanz existiert; und dass das Singleton-Objekt später auch die Verwendung in vielen parallelen Abläufen erlaubt, also threadsicher ist.
- Werden dynamische Bibliotheken verwendet, muss u. U. sichergestellt werden, dass auch hier nur eine Instanz existieren kann.
- Ist der Singleton ein generischer Typ, wird vom Compiler dynamisch für jede verwendete Kombination an Typparametern eine eigene Klasse erzeugt. Dies kann korrekt und beabsichtigt sein, aber auch unerwartete Nebeneffekte haben, z. B. dass der Konstruktor und alle weiteren Initialisierungen von Singleton<T> mehrfach durchlaufen werden können, was der Logik eines gewöhnlichen Singletons oft widerspricht.
- Das Testen eines Singleton kann kompliziert sein. Das Mocken eines Singleton-Objekts ist aufwändig und in manchen Fällen – zum Beispiel, wenn für Testzwecke Fehler erzeugt werden sollen – fast unmöglich. Mit der Java Reflection API ist es jedoch möglich, die Kapselung der Singleton zu verletzen und die Instanziierung zu kontrollieren.
- Die Konfiguration des Singletons ist – zumindest bei Lazy-Initialization (s. u.) – nur über andere Singletons möglich, zum Beispiel Environment-Variablen, aus einem Registry, aus „well-known“ Files o. Ä.
- Eine Ressourcen-Deallokation von Ressourcen, die das Singleton verwendet, ist schwierig. So ist zum Beispiel bei einem Singleton für ein Logging-System oft unklar, wann die Logdatei geschlossen werden soll.
Wegen der vielen Nachteile wird das Singleton-Muster (und auch das Idiom Double-checked Locking) mitunter schon als Anti-Pattern bewertet. Für Fälle, in denen tatsächlich technisch ein passender Bereich für ein Singleton existiert, können Singletons aber sinnvoll sein – insbesondere wenn sie sich auf andere „einmalige Strukturen“ wie zum Beispiel eine Abstract Factory beziehen. Trotzdem: Das korrekte Design von Singletons ist schwierig – in der Regel schwieriger als Designs ohne Singletons.
Verwendung in der Analyse
[Bearbeiten | Quelltext bearbeiten]In der Analyse wird ein (fachliches) Singleton in der Regel dadurch gekennzeichnet, dass die Multiplizität der Klasse als 1 definiert wird. Wie auch im Design muss der Bereich der Multiplizität hinterfragt werden: Gibt es tatsächlich nur „eine Zentralstelle für …“, oder können zum Beispiel in länderübergreifenden Systemen sehr wohl mehrere Objekte einer Sorte existieren?
Implementierung
[Bearbeiten | Quelltext bearbeiten]Von Lazy Creation spricht man, wenn das einzige Objekt der Klasse erst erzeugt wird, wenn es benötigt wird. Ziel ist, dass der Speicherbedarf und die Rechenzeit für die Instanziierung des Objektes nur dann aufgewendet werden, wenn das Objekt wirklich benötigt wird. Hierzu wird der Konstruktor ausschließlich beim ersten Aufruf der Funktion getInstance()
aufgerufen.
Mit geeigneten Mitteln können Lazy Creation Singleton Implementierungen sicher hinsichtlich Nebenläufigkeit gemacht werden, indem die zentrale Methode getInstance
beispielsweise in Java mit dem Schlüsselwort synchronized
markiert wird. Eine einfachere Alternative dazu stellt jedoch die Möglichkeit dar, das Singleton bereits während der Initialisierung der Klasse zu erzeugen, die Zugriffsmethode muss es dann nur noch zurückgeben. Daher muss sie nicht synchronisiert werden, was den Zugriff etwas beschleunigt. Dieses Verfahren ist auch als eager creation (deutsch „begierige Erzeugung“) bekannt.
Wenn ein Singleton nicht von einer anderen Klasse abgeleitet werden muss, kann man die Klasse auch einfach als statisch deklarieren – das entspricht immer noch dem Singleton-Prinzip. Diese sogenannten Monostate-Klassen (auch Borg Pattern genannt) wurden von S. Ball und J. Crawford in ihrem Artikel „Monostate classes: the power of one“ vorgeschlagen.[4][5] Hier können beliebig viele Instanzen einer Klasse existieren, sie teilen sich jedoch einen gemeinsamen Zustand. In C++, Java und PHP kann dies leicht realisiert werden, indem alle Klassenattribute als static deklariert werden. Der Name Borg stammt aus einem Posting von Alex Martelli im ASPN und bezieht sich auf Star Trek.[6]
In Programmiersprachen die Enums unterstützen (wie beispielsweise Java ab Version 5) kann zur Implementierung des Singleton-Patterns das Sprachkonstrukt enum
genutzt werden, welches außerdem direkt die Serialisierbarkeit ermöglicht[7]. Damit wird aber ebenfalls eine Ableitung des Singletons von einer anderen Klasse unmöglich.
Möchte man eine bestimmte Anzahl Instanzen einer Klasse (Multiton), so kann man einfach weitere statische öffentliche Instanzfelder hinzufügen (public static readonly Singleton Instance2 = new Singleton();
).
Eine Alternative, welche die Instanz erst beim ersten Aufruf von getInstance()
erzeugt, ist unter dem Namen initialization on demand holder idiom bekannt. Dabei wird die Instanz in einer inneren Klasse Holder
erzeugt, welche erst beim ersten Aufruf von getInstance()
geladen wird, und nicht schon beim Laden der Klasse Singleton
.
Für Codebeispiele siehe Liste von Singleton-Implementierungen.
Verwandte Entwurfsmuster
[Bearbeiten | Quelltext bearbeiten]Die Eigenschaften des Singleton treffen für viele Klassen der anderen Muster zu, so dass diese dann als Singleton ausgeführt werden.
Zum Beispiel sind abstrakte Fabriken, Erbauer oder Prototypen oft auch Singleton.
Weblinks
[Bearbeiten | Quelltext bearbeiten]- Singleton ( vom 24. April 2002 im Internet Archive) (englisch)
- Beispiel für die Implementierung des Singleton-Patterns in Java
- Exploring the Singleton Design Pattern (englisch)
- Double Checked Locking is broken in Java (englisch)
- Javascript implementation of a Singleton Pattern ( vom 2. April 2010 im Internet Archive) (englisch)
- Beispiel für ein Singleton unter PHP5
- An Overview of the Java Memory Model (JMM)
- Singletonpattern inkl. Beachtung des Multithreadings
Einzelnachweise
[Bearbeiten | Quelltext bearbeiten]- ↑ Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides: Entwurfsmuster. 5. Auflage. Addison-Wesley, 1996, ISBN 3-8273-1862-9, S. 157.
- ↑ Singleton Considered Stupid
- ↑ Singletons are Pathological Liars
- ↑ S. Ball, J. Crawford: Monostate classes, ACM Digital Library
- ↑ Robert C. Martin: More C++ Gems, S. 223
- ↑ Alex Martelli: Singleton? We don’t need no stinkin’ singleton: the Borg design pattern. Posting im „ActiveState Programmer Network“ vom 27. August 2001 [22. Januar 2006]
- ↑ Joshua Bloch: Effective Java Second Edition. Item 3: Enforce the singleton property with a private constructor or an enum type