Singleton (Design Pattern): Unterschied zwischen den Versionen

Aus Byte-Welt Wiki
Zur Navigation springenZur Suche springen
(Nachteile)
(Quellen)
 
(2 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt)
Zeile 45: Zeile 45:
 
* Da der Konstruktor eines Singletons private ist kann man von ihm nicht erben.
 
* Da der Konstruktor eines Singletons private ist kann man von ihm nicht erben.
  
== Implementierung/Beispielcode ==
 
* In Systemen mit parallelen Abläufen ([[Thread]]s) muss sichergestellt sein, dass (a) nicht durch parallele Initialisierung kurzfristig mehr als eine Instanz existiert; und (b) dass das Singleton-Objekt später auch die Verwendung in vielen parallelen Abläufen erlaubt, also [[thread-safe]] ist.
 
** (a) wird in der Regel durch einen Semaphor um die Initialisierung erreicht (z. B. Java: mit '''synchronized'''). Aus Effizienzgründen verwendet man dazu in .Net das [[Double-checked Locking]] Idiom. In Java ist das wegen tiefliegender Ursachen im Speicherzugriffsmodell nicht möglich.
 
** (b) erfordert ein "[[thread-safe]] Design" der Singleton-Klasse.
 
* Die Konfiguration des Singletons ist - zumindest bei Lazy-Initialization (s.u.) - nur über andere Singletons möglich, z.B. Environment-Variablen, aus einem Registry, aus "well-known" Files o.ä.
 
* Eine Ressource-Deallokation von Ressourcen, die das Singleton verwendet, ist schwierig. So ist z.B. bei einem Singleton für ein Logging-System oft unklar, wann die Log-Datei geschlossen werden soll.
 
* In einigen [[objektorientierte Programmiersprache|objektorientierten Programmiersprachen]] gibt es keine Möglichkeit, Klassenmethoden zu schreiben.
 
* In [[Dynamic Link Library|DLL]]s ([[Dynamic Link Library|dynamic link libraries]]) lassen sich (zumindest in C++) Singletons nur eingeschränkt verwenden. Da DLLs nicht wie zum Beispiel [[Bibliothek|Libraries]] zum Programm gelinkt werden, sondern von Haus aus gelinkt sind, wird ein Singleton, das in einer DLL und dem Hauptprogramm verwendet wird, in beiden Modulen ein eigenes Objekt sein. Das kann man (umständlich) vermeiden, indem das Hauptprogramm die eigene Instanz des Singleton an die DLL übergibt (dies ist ein Spezialfall des oben erwähnten "Scope"-Problems).
 
  
Wegen der vielen Nachteile wird das Singleton-Muster (und auch das [[Double-checked Locking]] Idiom) mitunter schon als [[Anti-Pattern]] bewertet. Für Fälle, wo tatsächlich technisch ein passender Bereich für ein Singleton existiert (z.B. wenn nur ein einziges GUI von einem Programm ausgesteuert wird), sind Singletons aber sinnvoll - insbesondere wenn sie sich auf andere "einmalige Strukturen" wie z.B. eine [[Abstract Factory (Design Pattern) | Abstract Factory]] beziehen. Trotzdem: ''Das korrekte Design von Singletons ist schwierig - i. d. R. schwieriger als Designs ohne Singletons.''
+
==Beispiele==
 
+
*Siehe [[Singleton Beispiele (Design Pattern)]]
=== Verwendung in der Analyse ===
 
In der Analyse wird ein (fachliches) Singleton i. d. R. 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 z.B. in länderübergreifenden Systemen sehr wohl mehrere Objekte einer Sorte existieren?
 
 
 
===Lazy Creation===
 
Von Lazy Creation spricht man, wenn das einzige Objekt der Klasse erst erzeugt wird, wenn es benötigt wird.
 
 
 
=== Beispielcode ===
 
====Implementierung in [[Java]]====
 
 
 
Die Erstellung des einmalig existierenden Objekts wird folgendermaßen erreicht:
 
* Der Konstruktor der Singleton-Klasse ist privat. So ist es von außen nicht möglich, ein weiteres Objekt dieser Klasse zu erzeugen.
 
* Als Ersatz wird eine neue [[Zugriffsfunktion|Zugriffsmethode]] angelegt, die eine [[Referenz|Referenz]] auf das einzige Objekt zurückgeben kann.
 
* Die [[Variable]], in der das Objekt gespeichert wird, erhält den Modifikator „statisch“ („<code>static</code>“). Sie ist außerdem synchronisiert um die Sicherheit bei nebenläufiger Ausführung zu gewährleisten. Eine alternative Implementierung, die ohne Synchronisierung auskommt, ist unten unter ''[[#Eager Creation|Eager Creation]]'' gezeigt. In [[Java (Programmiersprache)|Java]] wird auch dabei das Objekt erst erzeugt wenn es gebraucht wird.
 
<code=Java>
 
  public final class Singleton {
 
 
      /**
 
      * Privates Klassenattribut,
 
      * wird beim erstmaligen Gebrauch (nicht beim Laden) der Klasse erzeugt
 
      */
 
      private static Singleton instance;
 
 
      /** Konstruktor ist privat, darf nicht von außen instanziiert werden. */
 
      private Singleton() {}
 
 
      /**
 
      * Statische Methode "getInstance()" liefert die einzige Instanz der Klasse zurück.
 
      * Ist synchronisiert und somit thread-sicher.
 
      */
 
      public synchronized static Singleton getInstance() {
 
          if (instance == null) {
 
              instance = new Singleton();
 
          }
 
          return instance;
 
      }
 
 
  }
 
</code=Java>
 
 
 
====Implementierung in [[Cpp | C++]]====
 
 
 
Vorsicht: Die beiden folgenden Implementierungen sind nicht korrekt, da mehr als nur eine Instanz erstellt werden kann, sofern mehrere Threads gleichzeitig getInstance() aufrufen. Sie dienen nur als Beispiel und sollten in dieser Form ohne Locking nicht in großen Projekten verwendet werden [http://www.oaklib.org/docs/oak/singleton.html SINGLETON PATTERN - THREAD SAFETY].
 
 
 
Erste Methode: hat den Vorteil, dass bei Programmende das Objekt automatisch zerstört wird (und der Destruktor aufgerufen wird). Darüber, wann das passiert, hat man aber keine Kontrolle.
 
<code=Cpp>
 
  class Singleton
 
  {
 
    private:
 
      //Konstruktor private, damit man sich keine Instanzen holen kann.
 
      Singleton();
 
      //Den Kopierkonstruktor schützen um zu vermeiden, dass das Objekt unbeabsichtigt kopiert wird.
 
      Singleton(const Singleton& cc){}
 
 
 
    public:
 
      ~Singleton();
 
      static Singleton* getInstance();
 
  };
 
 
 
  Singleton* Singleton::getInstance()
 
  {
 
    static Singleton instance;
 
    return &instance;
 
  }
 
</code=Cpp>
 
 
 
Zweite Methode: Vorteil dieser ist, dass man bei Erzeugung des Objektes ein spezialisiertes Objekt erzeugen kann, man hat also [[Polymorphie]]. Außerdem hat man durch Hinzufügen einer statischen Destroy()-Funktion volle Kontrolle darüber, wann das Singleton wieder zerstört wird.
 
<code=Cpp>
 
  class Singleton
 
  {
 
    private:
 
      //Ein Pointer zum Speichern der Instanz
 
      static Singleton* instance;
 
      //Konstruktor private, damit man sich keine Instanzen holen kann.
 
      Singleton();
 
      //Den Kopierkonstruktor schützen um zu vermeiden, dass das Objekt unbeabsichtigt kopiert wird.
 
      Singleton(const Singleton& cc){}
 
      ~Singleton();
 
    public:
 
      static Singleton* getInstance();
 
      static void Destroy(); 
 
};
 
 
 
  Singleton* Singleton::instance = 0;
 
 
 
  Singleton* Singleton::getInstance()
 
  {
 
    if( !instance )
 
      instance = new Singleton();
 
    return instance;
 
  }
 
 
 
  void Singleton::Destroy()
 
  {
 
  // static
 
  delete instance;
 
  instance= 0;
 
  }
 
</code=Cpp>
 
 
 
====Implementierung in [[PHP]] (ab Version 5)====
 
<code=php>
 
<?php
 
class Singleton {
 
  /**
 
    * die Instanz wird hier abgelegt
 
    */
 
  private static $instance = NULL;
 
 
 
  /**
 
    * Konstruktor private, damit die Klasse nur aus sich selbst heraus instanziiert werden kann.
 
    */
 
  private function __construct() {}
 
 
  /**
 
    * mit dieser statischen Methode bekommt man das Singleton
 
    */
 
  public static function getInstance()
 
  {
 
      if (self::$instance === NULL)
 
      {
 
          self::$instance = new Singleton;
 
      }
 
      return self::$instance;
 
  }
 
 
  /**
 
    * Klonen per 'clone' von außen verbieten
 
    */
 
  private function __clone() {}
 
}
 
 
$single = Singleton::getInstance();
 
?>
 
</code=php>
 
 
 
===Eager Creation===
 
 
 
Zwar ist auch das oben gezeigte Beispiel sicher bezüglich [[Nebenläufigkeit]], weil die zentrale Methode <code>getInstance</code> mit dem Java-Schlüsselwort <code>synchronized</code> markiert ist. 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.
 
 
 
====Implementierung in [[Java]]====
 
In [[Java]] ist diese Implementierung dem Lazy Creation Ansatz vorzuziehen. Die Initialisierung findet durch das späte Initialisieren der Klasse erst statt, wenn die Klasse <code>Singleton</code> referenziert wird.<ref>{{Literatur|Autor=Tim Lindholm, Frank Yellin|Titel=The JavaTM TMVirtual Machine Specification|Auflage=2.|Seiten=Abschnitt 2.17.4|Online=[http://java.sun.com/docs/books/vmspec/2nd-edition/html/Concepts.doc.html#19075 online] |Zugriff=22. Januar 2007}}</ref> Da der Zugriff nur via <code>getInstance()</code> erfolgt ist dies auch der spätest mögliche Zeitpunkt und entspricht somit der Lazy Evaluation ohne den Synchronisierungs-Overhead.
 
<code=Java>
 
  public final class Singleton {
 
 
      /** Privates Klassenattribut, einzige Instanz der Klasse wird erzeugt. */
 
      private static final Singleton INSTANCE = new Singleton();
 
 
      /** Konstruktor ist privat, darf nicht von außen instanziiert werden. */
 
      private Singleton() {}
 
 
      /** Statische Methode "getInstance()" liefert die einzige Instanz der Klasse zurück. */
 
      public static Singleton getInstance() {
 
          return INSTANCE;
 
      }
 
 
}
 
</code=Java>
 
 
 
====Implementierung in C#====
 
Eine Thread-sichere Methode entnommen aus dem MSDN-Mag<ref>http://msdn.microsoft.com/msdnmag/issues/03/02/CQA/</ref>.
 
<code=Csharp>
 
  using System ;
 
 
 
  sealed class Singleton
 
  {
 
    private Singleton(){}
 
    public static readonly Singleton Instance = new Singleton() ;
 
  }
 
 
 
  // Zugriff über
 
  Singleton s = Singleton.Instance ;
 
</code=Csharp>
 
 
 
====Implementierung eines Generics für Singletons in C# ab .Net 2.0====
 
 
 
Mit dieser Implementierung muss das Singleton nur einmal implementiert werden.
 
Auf seiner Basis können dann leicht mehrere verschiedene Singletons erzeugt werden:
 
 
 
<code=Csharp>
 
  using System;
 
  using System.Threading;
 
  public class Singleton<T, X>
 
    where T : new()
 
    where X : class
 
  {
 
    private static T      m_Instance;
 
    private static Object m_SyncObject;
 
    static Singleton()
 
    {
 
    }
 
    public static implicit operator X(Singleton<T, X> t)
 
    {
 
        return t as X;
 
    }
 
    public static X Instance
 
    {
 
        get
 
        {
 
            Monitor.Enter(m_SyncObject);
 
            if (m_Instance == null)
 
            {
 
                m_Instance = new T();
 
            }
 
            Monitor.Exit(m_SyncObject);
 
            return m_Instance as X;
 
        }
 
    }
 
</code=Csharp>
 
 
 
Ein konkreter Singleton würde dann in etwa so aussehen:
 
<code=Csharp>
 
  class MeinSingleton : Singleton<MeinSingleton, MeinSingleton>
 
  {
 
  }
 
</code=Csharp>
 
 
 
Der Zugriff erfolgt hier über:<code>MeinSingleton.Instance</code>
 
 
 
===Das Borg Pattern===
 
Unter Python gibt es auch mehrere Ansätze für das Singleton Entwurfsmuster, allerdings wird hier allgemein eher das Borg Pattern angewendet:
 
<code=python>
 
class Borg(object):
 
    _shared = {}
 
    def __new__(cls,*args,**kwargs):
 
        inst = object.__new__(cls)
 
        inst.__dict__ = cls._shared
 
        return inst
 
</code=python>
 
Im Gegensatz zum Singleton können mehrere Instanzen erstellt werden, allerdings teilen sich alle Instanzen der Borg Klasse ihre Attribute.
 
 
 
Der Name Borg stammt aus einem Posting von Alex Martelli im [[ASPN]] und bezieht sich auf Star Trek.<ref>Alex Martelli: ''[http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66531 Singleton? We don't need no stinkin' singleton: the Borg design pattern.]'' Posting im „ActiveState Programmer Network“ vom 27. August 2001 [22. Januar 2006]</ref>
 
  
 
==Verwandte Entwurfsmuster==
 
==Verwandte Entwurfsmuster==
 
 
Die Eigenschaften des Singleton treffen für viele Klassen der anderen Muster zu, so dass diese dann als Singleton ausgeführt werden.
 
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 [[Abstract Factory (Design Pattern) | Abstract Factory]], [[Builder (Design Pattern) | Builder]] oder [[Prototype (Design Pattern) | Prototype]] oft auch Singleton.
 
Zum Beispiel sind [[Abstract Factory (Design Pattern) | Abstract Factory]], [[Builder (Design Pattern) | Builder]] oder [[Prototype (Design Pattern) | Prototype]] oft auch Singleton.
  
== Quellen ==
+
 
<references/>
 
  
 
== Weblinks ==
 
== Weblinks ==
Zeile 303: Zeile 62:
 
* [http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/singletondespatt.asp Exploring the Singleton Design Pattern] (englisch)
 
* [http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/singletondespatt.asp Exploring the Singleton Design Pattern] (englisch)
 
* [http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html Double Checked Locking is broken] in Java (englisch)
 
* [http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html Double Checked Locking is broken] in Java (englisch)
 
{{Wikipedia|Seite=Einzelstück_(Entwurfsmuster)}}
 
  
 
[[Kategorie:Design Pattern]]
 
[[Kategorie:Design Pattern]]
 
[[Kategorie:Creational Pattern]]
 
[[Kategorie:Creational Pattern]]
 
[[Kategorie:Gang of Four Pattern]]
 
[[Kategorie:Gang of Four Pattern]]

Aktuelle Version vom 16. September 2008, 13:36 Uhr

Das Singleton, im Deutschen auch Einzelstück genannt, ist ein in der Softwareentwicklung eingesetztes Entwurfsmuster und gehört zur Kategorie der Creational Pattern (Erzeugungsmuster). Das Muster ist eines der von der so genannten Viererbande (GoF) publizierten Muster.

Zweck

Es stellt sicher, dass zu einer Klasse nur genau ein Objekt erzeugt werden kann und ermöglicht einen globalen Zugriff auf dieses Objekt.

Motivation

Das Singleton findet Verwendung, wenn

  • nur ein Objekt zu einer Klasse existieren darf und ein einfacher Zugriff auf dieses Objekt benötigt wird oder
  • wenn das einzige Objekt durch Unterklassenbildung spezialisiert werden soll.

Anwendbarkeit

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.

Struktur

Singleton
instance : Singleton
– Singleton()
+ getInstance() : Singleton

Teilnehmer/Interaktion

In Klammer stehen die Bezeichnungen aus der Abbildung oben.

  • Singleton
    • erzeugt und verwaltet einziges Objekt zu einer Klasse
    • bietet globalen Zugriff auf dieses Objekt über eine Instanzoperation
    • die Instanzoperation ist eine Klassenmethode, d. h. statisch gebunden
    • (das Attribut „Instanz“ (instance) ist ein Klassenattribut, d. h. ein statisches Attribut)

Konsequenz

Vorteile

  • Das Muster bietet eine Verbesserung gegenüber globalen Variablen.
  • Das Singleton kann durch Unterklassenbildung spezialisiert werden.
  • Sollten später mehrere Objekte benötigt werden, ist eine Änderung leicht(er) möglich.

Nachteile

  • Es besteht die große Gefahr, durch exzessive Verwendung von Singletons quasi ein objektorientiertes Äquivalent zu globalen Variablen zu implementieren.
  • Der "Scope" eines Singletons - d.h. in welcher Umgebung ist das Singleton tatsächlich "einzeln"? - ist i. d. R. durch die Ablaufumgebung definiert, die nicht mit dem Bereich zusammenfallen muss, in dem das Singleton technisch oder fachlich "einzeln" sein soll. Z.B. ist in Java eine einfache static-Variable "einzeln je ClassLoader"; in verteilten (z.B. cluster-fähigen) Systemen oder komplexen nebenläufigen Applikationen wie Application-Servern können dadurch wieder mehrere Instanzen nebeneinander existieren. Analoges gilt für die so genannten App-Domains in .Net. Im Zusammenhang mit Generischen Typen kann die Semantik von static-Variablen nocheinmal komplexer sein (und ist etwa in Java und C# unterschiedlich!).
  • Die Testability von Singletons ist schlecht. Das Mocken eines Singleton-Objekts, z.B. eines Netzwerkzugriffs ist aufwändig und in manchen Fällen - z.B. wenn für Testzwecke Fehler erzeugt werden sollen - fast unmöglich.
  • Da der Konstruktor eines Singletons private ist kann man von ihm nicht erben.


Beispiele

Verwandte Entwurfsmuster

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 Abstract Factory, Builder oder Prototype oft auch Singleton.


Weblinks