B) Singleton

Ein Singleton ist ein objektbasiertes Erzeugermuster und stellt sicher, dass nur ein Exemplar einer Klasse besteht, auf welches global zugegriffen werden kann.

In manchen Fällen kann es wichtig sein, dass nur genau ein Objekt einer Klasse im ganzen Programm existiert. So kann es bspw. in einem System viele Drucker geben, aber nur einen Druckerspooler. Natürlich könnten Sie für einen solchen Fall ein öffentliches Attribute bzw. ein privates Attribut mit entsprechenden Gettern und Settern anlegen. Aber irgendwie müssen Sie von überall (ohne, dass ein bestimmtes Objekt vorhanden ist) auf dieses Attribut zugreifen können. Und wie stellen Sie sicher, dass nicht einfach ein neues Objekt der Klasse erzeugt wird?

Dies funktioniert am Ehesten, wenn eine Klasse selbst ihre eigenen Zugriffsmöglichkeiten steuert. Sie hat einen privaten Konstruktor, so dass von Außen kein neues Objekt von ihr erzeugt werden kann. Außerdem hat sie eine statische, private Instanz von sich selbst. Auf diese Instanz kann über einen entsprechenden, statischen Getter zugegriffen werden. Schon sind die Anforderungen an einen Singleton erfüllt. Machen Sie sich den Sachverhalt nochmals am Diagramm deutlich:

Singleton

So stellen Sie sicher, dass es genau ein Exemplar einer Klasse gibt, und dass dieses von überall aus erreichbar ist. Seien Sie aber bei der Verwendung von Singletons umsichtig. Es besteht die Gefahr, dass Sie bei exzessiver Verwendung dieses Patterns die Objektorientierung umgehen und aushebeln – was nicht in Ihrem Interesse liegen sollte.

Ansätze

Es gibt zwei Ansätze, um einen Singleton umzusetzen. Mit der Lazy Creation wird das Exemplar erst dann erzeugt, wenn die getInstance-Methode das erste Mal aufgerufen wurde. Wenn kein Objekt benötigt wird, wird auch keine Instanz erzeugt. Bei dieser Variante muss getInstance synchronisiert sein, um die zweimalige Generierung der Instanz durch einen zeitgleichen Zugriff zweier Threads zu unterbinden.

Der andere Ansatz ist die Eager Creation. Hierbei wird die statische Instanz sofort angelegt und nicht erst beim Aufruf der getInstance-Methode. Bei diesem Konzept muss die Zugriffsmethode folglich nicht synchronisiert werden. Meistens ist die Eager Creation der Lazy Creation vorzuziehen, da in Java auch bei der Eager Creation erst das Objekt erzeugt werden würde, wenn das erste Mal auf die Singleton-Klasse zugegriffen wird.

Implementierung

Beide Ansätze setzen ein privates, statisches Attribut der Klasse selbst voraus. Zusätzlich wird ein privater Konstruktor und eine öffentliche, statische Methode getInstance benötigt.

public class Singleton {

  private static Singleton INSTANCE;

  private Singleton() {}

  public static Singleton getInstance() {
    return null;
  }
}

Bei der Lazy Creation müssen Sie lediglich die getInstance-Methode anpassen. Sie muss synchronisiert sein und testen, ob INSTANCE null ist. Falls dies zutrifft, wird das INSTANCE-Attribute initialisiert und anschließend zurückgegeben.

public class Singleton {

  private static Singleton INSTANCE;

  private Singleton() {}

  public synchronized static Singleton getInstance() {
    if (Singleton.INSTANCE == null) {
      Singleton.INSTANCE = new Singleton();
    }
    return Singleton.INSTANCE;
  }
}

Die Eager Creation hingegen erfordert eine direkte Initialisierung bei der Deklarierung unserer einzigen Instanz von Singleton. Entsprechend kann das Prozedere in der getInstance-Methode verkürzt werden:

public class Singleton {

  private static Singleton INSTANCE = new Singleton();

  private Singleton() {}

  public static Singleton getInstance() {
    return Singleton.INSTANCE;
  }
}

Singleton in Stichpunkten

In diesem Pattern gibt es nur einen Teilnehmer

  • Singleton => stellt ein statisches Objekt von sich selbst und eine statische Methode bereit, die es erlaubt, von Außen an die einzige Instanz zu gelangen.

, deren Implementierung bestimmten Grundregeln unterliegt:

  • Es darf nur ein Objekt der Singleton-Klasse existieren.
  • Dieses Objekt muss global verfügbar gehalten werden.
  • Der Zugriff auf dieses Objekt erfolgt ausschließlich über die entsprechende Get-Methode der Singleton-Klasse.

Verwendet wird der Singleton primär, wenn:

  • Genau ein Objekt einer Klasse existieren muss, und auf selbiges global zugegriffen werden kann.

Hieraus resultieren gewisse Konsequenzen:

  • Die Zugriffskontrolle auf das einzige Objekt dieser Klasse wird von der Klasse selbst gesteuert.
  • Ermöglichung einer Art globaler Variablen in Java. (Wobei an dieser Stelle dahingestellt sei, ob dies immer sinnvoll ist.)
  • Schnelle Änderung des Konzepts: Sollten Sie doch mehrere Instanzen der Klasse benötigen, genügt es, wenn Sie jedes Mal beim Aufruf von getInstance eine neue Instanz der Klasse zurückgeben. Der restliche Code bleibt unberührt.

Kritik am Singleton-Pattern

Mittlerweile sind einige Entwickler und Architekten der Meinung, dass es sich beim Singleton nicht um ein Design-Pattern, sondern um ein Anti-Pattern handelt. Ein Muster, wie man es gerade nicht machen sollte. Dabei wird gerne u. a. auf folgende Kritikpunkte verwiesen:

  • Da die Zugriffe durch die globale Verfügbarkeit nicht mehr kontrolliert werden können, wird die Testbarkeit sehr eingeschränkt.
  • Verletzung des Single Responsibility Prinzips.
  • Ein Singleton wird in den meisten Fällen als „globaler Datenspeicher“ zweckentfremdet. Das eigentliche Ziel, dass es nur ein Objekt der Klasse im ganzen Programm gibt, wird aus den Augen verloren.
  • Verschleiern von Abhängigkeiten.

Mit Blick auf den Singleton, sollten Sie noch folgende Pattern beachten, die mit Hilfe eines Singletons implementiert werden könnten:

4 Replies to “B) Singleton”

  1. Marty

    der kommentar muss leider sein 😉

    >Aber irgendwie müssen Sie von überall (ohne, dass ein bestimmtes >Objekt vorhanden ist) auf dieses Attribut zugreifen können.
    Nennt sich auch globale Variable und sind absolut zu vermeiden. Wenn eine Klasse auf etwas zugreifen muss, so soll diese als Abhaengigkeit mitgeteilt werden, nicht ueber irgendwelche magic access der Klasse selber. Das hat mehrere Probleme:

    im Grunde luegt das Programm bzw die API. Es werden explizit keine Abhaengigkeiten deklariert, aber aufgrund von globalen Zugriffen hat man auf einmal einen Objektgraphen mitzukompilieren. Es werden sozusagen Abhaengigkeiten verschleiert.

    untestbar: man will in seinen Tests Objekte erzeugen, testen, wegschmeissen – weiterhin will man allgemein dass ein stueck code bei wiederholten aufruf, ohne informationsfluss von aussen, immer das selbe tut… durch singletons bzw globale zustaende ist dies nicht gesichert.

    Test klappt, Test klappt nicht, Test klappt, Test klappt nicht…
    Man hat eine Testsuite die 1h laeuft. Der letzte Test schlaegt fehl. In Isolation geht er durch… viel Spass das zu debuggen.

    Weiterhin hat man durch Singletons nicht nur eine globale Variable, da meist das Singleton selbst noch Referenzen haelt und diese wieder und diese wieder, was eben so theoretisch unendliche vielen globalen Zustaenden sich ausweitet.

    >Und wie >stellen Sie sicher, dass nicht einfach ein neues Objekt der >Klasse erzeugt >wird?
    In dem man einfach Objekterzeugung aus der Logik rausnimmt. Kein logikcode, keine datenklasse erzeugt irgendwelche Objekte, es wird alles per Konstruktor (bzw methoden) explizit reingegeben (stichwort Dependency Injection).
    Es gibt 1 Stelle, und nur 1 Stelle an den Objekte erzeugt werdene und verdrahtet werden.

    Das Singleton verletzt das SingleResponsiblity Prinzip, es ist sowohl fuer logik zustaendig, als auch sicherzustellen, dass es die Instanz nur einmal gibt. Nicht die Klasse hat das zu regeln, sondern die Applikation.

    D.h. es ist nicht verwerflich von einer Klasse nur eine Instanz in seiner Applikation zu haben, es ist aber verwerflich wenn die Klasse diese Einzigartigkeit selbst erzwingt.

    Summasumarum: Singletons sind nicht zu verwenden und eine Designschwaeche. In 9 von 10 Faellen wird es nicht aufgrund seiner Einzigartigkeit genommen (wofuer es bessere alternativen gibt), sondern eben genau wegen „muss ueberall zugreifen koennen“ – der groesse Denk und designfehler.

    unbedingt empfehlenswert: http://www.youtube.com/watch?v=-FRm3VPhseI

  2. Stefan Kiesel

    Hallo Marty,

    danke für den umfangreichen und informativen Kommentar.

    Ich versuche erst gar nicht, die einzelnen Punkte zu entkräften oder denen entgegenzuwirken. Einfach deshalb nicht, weil Sie in den meisten Fällen recht haben.

    Ein Singleton macht schlichtweg in den wenigsten Situationen Sinn (auch wenn ich nicht so weit gehen möchte und behaupten, dass ein Singleton wirklich nie sinnvoll ist). Dennoch ist es ein relativ verbreitetes Pattern, welches auch im Buch der GoF zu finden ist, weshalb es letztendlich auch den Weg in das Java Blog Buch gefunden hat.

    Mit Ihrer Genehmigung würde ich Ihre Kritikpunkte gerne (leicht modifiziert) in das Kapitel einarbeiten.

    Vielen Dank und Grüße
    Stefan

  3. Marty

    Danke fuer die Antwort.

    Mir ist durchaus bekannt, dass es ein „offizielles“ Pattern der GoF ist, was aber meine Kritik nicht schmaelert.

    Natuerlich koennen Sie meine Kritikpunkte in den Thread einarbeiten.

    Danke

Schreibe einen Kommentar

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.