Die so­ge­nann­ten Design Patterns (Ent­wurfs­mus­ter) helfen in der ob­jekt­ori­en­tier­ten Pro­gram­mie­rung den Ent­wick­lern mit vielfach bewährten Vorlagen für die Lösung von Pro­gram­mier­auf­ga­ben. Hat man aus den circa siebzig Ent­wurfs­mus­tern das geeignete gefunden, wird es durch in­di­vi­du­el­le An­pas­sun­gen ver­fei­nert. Dabei bleibt der generelle Denk­an­satz für das Muster aber erhalten. Das Singleton-Ent­wurfs­mus­ter ist sehr leis­tungs­fä­hig, hat al­ler­dings den Ruf, ein Relikt in der ob­jekt­ori­en­tier­ten Pro­gram­mie­rung zu sein. Was es trotzdem kann, wo und wie es genutzt wird, erfahren Sie in unserem Ratgeber.

Was ist das Singleton Pattern?

Das Singleton Pattern gehört zur Kategorie der Er­zeu­gungs­mus­ter unter den Design Patterns. Eine seltener ver­wen­de­te Be­zeich­nung ist „Ein­zel­stück“. Seine Aufgabe besteht darin, zu ver­hin­dern, dass von einer Klasse mehr als ein Objekt erstellt werden kann. Das wird dadurch erreicht, dass das ge­wünsch­te Objekt in einer Klasse selbst erzeugt dann als statische Instanz abgerufen wird. Das Singleton zählt zu den ein­fachs­ten, aber dafür mäch­tigs­ten Patterns in der Software-Ent­wick­lung.

Zitat

Die „Gang of Four“ (GfO) – ein Team von Pro­gram­mie­rern aus den USA – über das Singleton Pattern: „Sichere ab, dass eine Klasse genau ein Exemplar besitzt, und stelle einen globalen Zu­griffs­punkt darauf bereit.“

Was sind die Ei­gen­schaf­ten des Singleton Patterns?

Wurde mit dem Singleton-Ent­wurfs­mus­ter von einer Klasse eine Instanz erstellt, sorgt es dafür, dass es auch wirklich nur bei dieser einzelnen Instanz bleibt. Das Singleton macht diese Klasse in der Software global zu­gäng­lich. Dafür gibt es in den Pro­gram­mier­spra­chen ver­schie­de­ne Methoden. Damit es bei nur einer ein­zig­ar­ti­gen Instanz bleibt, muss ver­hin­dert werden, dass Nutzer neue Instanzen erzeugen können. Das geschieht, indem der Kon­struk­tor das Muster als „private“ de­kla­riert. Damit kann aus­schließ­lich der Code im Singleton das Singleton selbst in­stan­zi­ie­ren. Somit ist ga­ran­tiert, dass immer nur ein und dasselbe Objekt zum Nutzer gelangen kann. Besteht diese Instanz bereits, wird auch keine neue erzeugt. Ein mögliches Singleton sieht wie folgt aus:

public class Singleton {
	private static Singleton instance; // vor Zugriff von außen geschützt und statisch
	private Singleton() {} // privater Konstruktor mit Zugriffsschutz von außen
	public static Singleton getInstance() { // öffentliche Methode, Aufruf durch Code
		if (instance == null) { // nur wenn keine Instanz besteht, dann erstelle eine neue
			instance = new Singleton();
		}
		return instance;
	}
}

Das Singleton Pattern in UML-Dar­stel­lung

In der Dar­stel­lung mit der ver­ein­heit­lich­ten Mo­del­lie­rungs­spra­che, kurz UML (Unified Modeling Language), besteht das komplette Singleton Design Pattern nur aus einem einzigen Objekt, denn es ist ja auch nur eine singuläre Instanz einer Klasse zu erstellen.

Es ist von außen nicht möglich, an dem erzeugten Ein­zel­stück etwas zu verändern. Genau das ist das Ziel des Einsatzes des Singleton Design Patterns.

Vor- und Nachteile des Singleton-Ent­wurfs­mus­ters

Vorteile auf einen Blick

Ein Singleton lässt sich schnell und un­kom­pli­ziert schreiben, da es nicht mit un­zäh­li­gen (globalen) Variablen bestückt wird. Es kapselt seine Er­stel­lung und kann damit auch genaue Kontrolle ausüben, wann und wie darauf zu­ge­grif­fen wird. Ein be­stehen­des Singleton Pattern kann mittels Un­ter­klas­sen ab­ge­lei­tet werden, um neue Funk­tio­na­li­tä­ten aus­zu­fül­len. Was davon genutzt wird, wird dynamisch ent­schie­den. Und nicht zuletzt wird ein Singleton genau dann erzeugt, wenn es benötigt wird – diese Ei­gen­schaft trägt die Be­zeich­nung Lazy Loading. Der Vorgang, ein Singleton schon vor­ge­zo­gen zu in­stan­zi­ie­ren – also bevor es benötigt wird – heißt Eager Loading.

Nachteile auf einen Blick

Die un­ge­hemm­te Ver­wen­dung von Sin­gle­tons führt faktisch zu einem Zustand wie beim pro­ze­du­ra­len Pro­gram­mie­ren (also dem nicht ob­jekt­ori­en­tier­ten) und kann zu un­sau­be­rem Pro­gramm­code führen. Die globale Ver­füg­bar­keit von Singleton-Ent­wurfs­mus­tern birgt Gefahren, wenn sensible Daten damit behandelt werden. Werden Än­de­run­gen am Singleton vor­ge­nom­men, ist nicht nach­voll­zieh­bar, welche Pro­gramm­tei­le davon betroffen sind. Das erschwert die Wartung der Software, weil Fehl­funk­tio­nen nur schwer zu­rück­ver­folgt werden können. Die globale Ver­füg­bar­keit macht auch das Löschen von Sin­gle­tons schwierig, da immer Be­stand­tei­le einer Software auf dieses Singleton verweisen können. Bei An­wen­dun­gen mit vielen Benutzern (Mehr­be­nut­zer­an­wen­dun­gen) kann ein Singleton die Per­for­mance senken, denn es stellt – da eben singulär – eine Da­ten­eng­stel­le dar.

Das Singleton-Ent­wurfs­mus­ter im „echten Leben“

Das Singleton kommt meist dann zum Einsatz, wenn immer wie­der­keh­ren­de Aufgaben in einer Pro­gramm­rou­ti­ne zu erledigen sind. Dazu gehören Daten, die in eine Datei zu schreiben sind, z. B. beim Logging, oder es sind Druck­auf­trä­ge, die immer wieder in einen einzelnen Drucker-Puffer ge­schrie­ben werden sollen. Weil auch Treiber und Cache­me­cha­nis­men stets gleiche Abläufe aufweisen, wird auch dort gern das Singleton Design Pattern verwendet.

Da es sehr schwierig ist, das Singleton Pattern zu testen, ver­deut­li­chen wir hier seine Ar­beits­wei­se am Beispiel einer kleinen Fir­men­re­prä­sen­tanz, in der mehrere Mit­ar­bei­ter einen Drucker benutzen. Ein Beispiel, das der Praxis nahekommt, stellt die Design Pattern Tutorial Series von Daniel H. Jacobsen vor. Daran ori­en­tiert sich das nach­fol­gen­de Singleton-Ent­wurfs­mus­ter.

Schickt ein Nutzer eine Anfrage an den Drucker, wird vom Singleton die „Frage“ gestellt: „Gibt es schon ein Printer-Objekt? Wenn nicht, dann erstelle eins.“ Gelöst wird das mit einer Anweisung im if/then-Charakter (return Drucker == Null ?). Um den Zugriff und damit Ver­än­de­run­gen zu ver­hin­dern, werden einzelne Variablen und der Drucker auf „private“, also nicht „public“, gesetzt.

public class Drucker {
	private static Drucker drucker;
	private int AnzahlSeiten;
	private Drucker() {
	}
	public static Drucker getInstance() {
		return drucker == Null ? 
				drucker = new Drucker() : 
				drucker;
	}
	public void print(String text){
		System.out.println(text +
				"\n" + "Anzahl der heute gedruckten Seiten" + ++ AnzahlSeiten +
				"\n" + "---------");
	}
}

Als nächstes werden die Mit­ar­bei­ter der Nie­der­las­sung „gekapselt“. Die Strings für die Namen, die Position und die Tätigkeit im Un­ter­neh­men sind ebenfalls auf „private“ gesetzt.

public class Employee {
	private final String name;
	private final String position;
	private final String taetigkeit;
	public Mitarbeiter(String name, String position, String taetigkeit) {
		this.name = name;
		this.position = position;
		this.taetigkeit = taetigkeit;
	}
	public void printCurrent taetigkeit (){
		Drucker drucker = Drucker.getInstance();
		drucker.print("Mitarbeiter: " + name + "\n" +
			"Position: " + position + "\n" +
			"Taetigkeit: " + taetigkeit + "\n");
	}
}

Schließ­lich werden die beiden Sin­gle­tons in eine Aus­ga­be­rou­ti­ne ein­ge­bun­den.

public class Main {
	public static void main(String[] args) {
		Mitarbeiter andreas = new Mitarbeiter ("Andreas",
				"Chef",
				"Leitet die Niederlassung");
		Mitarbeiter julia = new Mitarbeiter ("Julia",
				"Beraterin",
				"Berät Kunden bei Reklamationen");
		Mitarbeiter tom = new Mitarbeiter ("Tom",
				"Verkäufer",
				"Verkauft die Produkte");
		Mitarbeiter stefanie = new Mitarbeiter ("Stefanie",
				"Entwicklerin",
				"Wartung der IT in der Niederlassung.");
		Mitarbeiter matthias = new Mitarbeiter ("Matthias",
				"Buchhalter",
				"Finanzbuchhaltung der Niederlassung.");
		andreas.printCurrentTaetigkeit();
		julia.printCurrentTaetigkeit ();
		tom.printCurrentTaetigkeit ();
		stefanie.printCurrentTaetigkeit ();
		matthias.printCurrentTaetigkeit ();
	}
}
Zum Hauptmenü