Singletons und Demanding Server

Singletons


Singletons sind in der professionellen Softwareentwicklung eine Standardtechnik geworden, die vielleicht auch bei Hobbyprogrammierern auf breiteres Interesse stoßen könnten. Was sind nun Singletons?

Singletons sind Klassen, von denen nur ein einziges Mal ein Objekt erzeugt werden kann. Häufig wird beim Programmieren an vielen Stellen auf Objekte zugegriffen, die innerhalb eines Systems eindeutig sein müssen (dies können Schnittstellen sein, z.B. RS232, oder zu jeder anderen Komponente oder zu einer Datenbank. Es ist nicht wünschenswert, daß zwei Objekte hierzu existieren, die in Konflikt miteinander geraten könnten). Da es gute Sitte ist, globale Variablen oder Objekte zu meiden, sind Singletons das Mittel der Wahl.

Ihr Konstruktionsprinzip ist einfach. Ob eine Klasse als Singleton verwandt werden soll, wird bei ihrer Implementierung festgelegt. Es gelten folgende Prinzipien:

1.) Der Konstruktor einer Singleton-Klasse ist als "private" deklariert. Dies hat zur Folge, daß er nicht explizit von außen zur Ausführung gebracht werden kann (was z.B. in C++ beim Speicherallozieren mit "new" automatisch passiert)

2.) Stattdessen wird die Singleton-Klasse mit einer Create()-Methode ("public") ausgestattet. Diese Methode gibt einen Zeiger auf das Objekt zurück. Sie prüft, ob sie zuvor schon einmal ausgeführt wurde. War dies noch nicht der Fall, wird in Create() der Konstruktor ausgeführt (was innerhalb der Methode ja trotz private-Deklaration geht) und der vom Konstruktor zurückgelieferte Zeiger wird in einer privaten Member-Variablen gemerkt (ähnlich dem this-Pointer). Wurde hingegen die Create()-Methode nicht zu ersten Mal ausgeführt, wird auf die Ausführung des Konstruktors verzichtet und der gemerkte Zeiger zurückgegeben.

3.) Beim Benutzen eines Singletons wird zwar der Zeiger auf ein Objekt der Singleton-Klasse deklariert, aber kein "new" zur Speicherallozierung aufgerufen (was auch gar nicht möglich ist, da der Konstruktor ja privat ist). Stattdessen wird Create() aufgerufen, wodurch der zuvor deklarierte Zeiger nun auf einen gültigen Speicherbereich zeigt und ohne Einschränkung benutzt werden kann.

4.) Die Speicherfreigabe erübrigt sich. Dies passiert einmalig, z.B. bei Beendigung der Anwendung.

Damit dies sinnvoll funktioniert, müssen sowohl Create() als auch der Pointer zum Merken des Zeigers vom Konstruktor statisch sein (in C++ also mit dem Schlüsselwort "static" versehen werden).

Singletons bieten eine klare Struktur für Zuständigkeiten. Die Logik für die Eindeutigkeit eines Objekts ist im Singleton gekapselt; der Benutzer eines Singletons muß sich darum keinerlei Gedanken mehr machen - womit wir wieder bei den guten Sitten des Programmierens wären.

Wer schon Erfahrung mit Objektorientierter Programmierung hat, ist bei diesem Thema etwas im Vorteil. Dennoch findet der Grundgedanke der Singletons auch bei strukturierter Programmierung Anwendung, denn das Hauptprinzip ist ja die Datenkapselung, also die Frage, welche Funktionen eines Softwaremoduls zugänglich gemacht werden oder nicht.

Musterimplementierung in verschiedenen Sprachen finden sich in der Wikipedia.

Demanding Server


Demanding Server gehen einen Schritt weiter. Sie kommen zur Anwendung, wenn bei der Objekterzeugung noch ein Parameter übergeben wird, der für die Eindeutigkeit der Objekte entscheidend ist. Solange noch kein Objekt mit identischem Parameter erzeugt wurde, wird es neu angelegt und zusammen mit dem Parameter in einer Liste oder noch besser in einem Objekt einer Containerklasse (vector in der Standard Typelib von C++ oder CList in .NET) gemerkt. Wird der Demanding Server mit einem bereits verwendeten Parameter aufgerufen, so wird kein neues Objekt erzeugt, sondern das bereits zuvor angelegte aus der Liste gesucht und zurückgegeben.

Dieses Prinzip funktioniert allerdings nur mit abzählbaren Parametertypen wie z.B. int oder enum, jedoch nicht mit float. Demanding Server können auf mehr als einen Parameter erweitert werden.

Der Vorteil ist auch hier, daß die Logik für die Eindeutigkeit komplett im Demanding Server verborgen bleibt und der Anwender des Demandng Servers sich überhaupt nicht darum kümmern muß, ob die von ihm angelegten Objekte wirklich eindeutig sind oder miteinander in Konflikt geraten können.

Für Demanding Server müssen im Gegensatz zu Singletons zwei Klassen implementiert werden: den Demanding Server selbst und die Klasse derjenigen Objekte, die vom Demanding Server zurückgegeben werden. Der Demanding Server selbst sollte als Singleton implementiert werden, um keine Konflikte bei der Objektverwaltung zu bekommen. Die Methode zum Anlegen/Heraussuchen des Zielobjekts ist wiederum "static".

Singletons zählen zu den Entwurfsmustern (Design Patterns), jene Standardrezepte für das Programmieren wiederkehrender Probleme darstellen, zu denen wir schon ein anderes mit dem Memento-Pattern kennengelernt hatten (s. die Links unten). Durch die Anwendung dieser Programmierprinzipien kann man sehr viele Fehlermöglichkeiten von vornherein ausschließen und sich das Leben als Programmierer erheblich erleichtern, bzw. sich an einer gewissen Eleganz der Lösung erfreuen.

Literatur: Gamma et al (Gang of Four), Entwurfsmuster, Addison-Wesley, 1996