Singleton Beispiele (Design Pattern): Unterschied zwischen den Versionen
K (→Implementierung in Actionscript (Version 2)) |
K (→Implementierung in Python (ab Version 2.2)) |
||
Zeile 243: | Zeile 243: | ||
==== Implementierung in [[Python (Programmiersprache)|Python]] (ab Version 2.2) ==== | ==== Implementierung in [[Python (Programmiersprache)|Python]] (ab Version 2.2) ==== | ||
− | < | + | <syntaxhighlight lang="phyton"> |
class Singleton(object): | class Singleton(object): | ||
def __new__(type, *args): | def __new__(type, *args): | ||
Zeile 257: | Zeile 257: | ||
# Einmalige Dinge wie z. B. die Initialisierung von Klassenvariablen müssen also in diesen Block. | # Einmalige Dinge wie z. B. die Initialisierung von Klassenvariablen müssen also in diesen Block. | ||
self._ready = True | self._ready = True | ||
− | </ | + | </syntaxhighlight> |
==== Implementierung in [[Perl (Programmiersprache)|Perl]] ==== | ==== Implementierung in [[Perl (Programmiersprache)|Perl]] ==== |
Version vom 8. März 2018, 19:25 Uhr
Beispiele für das Singleton.
Inhaltsverzeichnis
Lazy Creation
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.
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 Zugriffsmethode angelegt, die eine Referenz auf das einzige Objekt zurückgeben kann.
- Die Variable, in der das Objekt gespeichert wird, erhält den Modifikator „statisch“ („
static
“). 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 gezeigt. In Java wird auch dabei das Objekt erst erzeugt wenn es gebraucht wird.
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;
}
}
Nachteil dieser Variante ist, dass auch nach der Instanziierung jeder Lesezugriff über getInstance(); synchronisiert ist und so mehrere gleichzeitig zugreifende Threads sich gegenseitig blockieren. In Java gibt es hierfür keinen Workaround.
Wenn keine konkreten Gründe dagegen sprechen, ist daher in Java die wesentlich einfachere und effiziente #Eager Creation (siehe unten) vorzuziehen.
Implementierung in C#
ohne Threadsicherheit
// Nur zur Veranschaulichung - bitte nicht verwenden!
sealed class Singleton
{
private Singleton() { }
private static Singleton instance=null;
public static Singleton getInstance()
{
if(instance==null) instance=new Singleton();
return instance;
}
}
// Zugriff über Singleton s=Singleton.getInstance();
mit Threadsicherheit
sealed class Singleton
{
private Singleton() { }
private static volatile Singleton instance=null;
public static Singleton getInstance()
{
// DoubleLock
if(instance==null)
lock(m_lock) { if(instance==null) instance=new Singleton(); }
return instance;
}
// Hilfsfeld für eine sichere Threadsynchronisierung
private static Object m_lock = new Object();
}
// Zugriff über Singleton s=Singleton.getInstance();
Hinweise zum Code:
- Das private Hilfsobjekt m_lock ist in C# unbedingt notwendig. Die frühere gängige Methode lock(this) {..} hat sich als unzureichend herausgestellt, weil fehlerhafter oder böswilliger Code durch den Aufruf Monitor.Exit(Singleton); die Threadsicherheit einfach aushebeln kann.
- DoubleLock Methode: Warum zweimal dasselbe if()? Damit nach der Instanziierung des Objektes die bremsende Synchronisation per lock() wegfällt, wird dieselbe if-Abfrage zusätzlich vor dem lock-Abschnitt eingesetzt.
- Das Schlüsselwort volatile ist unbedingt notwendig. (s. The DOTNET Memory Model)
Falls es keine konkreten Einwände gibt, dass das Singleton-Objekt auch schon etwas früher instanziiert werden darf, ist der unter #Eager Creation beschriebene Code einfacher und schneller und daher in C# vorzuziehen.
Implementierung in 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.
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.
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;
}
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.
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;
}
Dritte Methode: Bietet eine Basisklasse um auf einfachste Weise eine Klasse als Singleton auszuweisen.
template <class T_DERIVED>
class CSingleton
{
public:
static T_DERIVED& GetInstance()
{
static T_DERIVED oInstance ;
return oInstance ;
}
protected:
CSingleton(){}
private:
CSingleton( const CSingleton& ) ;
CSingleton& operator=( const CSingleton& ) ;
} ;
// Verwendung
class CMySingleton : public CSingleton< CMySingleton >
{
friend class CSingleton< CMySingleton >;
private:
CMySingleton(){}
//...
}
Implementierung in Actionscript (Version 2)
class SingeltonClass
{
private static var _instance:SingeltonClass;
private function SingeltonClass()
{
}
/**/
public static function getInstance():SingeltonClass {
if(!_instance) _instance = new SingeltonClass();
return _instance;
}
/**/
public function doSomething()
{
}
}
/*
@use:
import SingeltonClass;
SingeltonClass.getInstance().doSomething();
*/
Implementierung in PHP (ab Version 5)
<?php
class Singleton {
// Anlegen der Instanz
private static $instance = NULL;
//Konstruktor private, damit die Klasse nur aus sich selbst heraus instanziiert werden kann.
private function __construct() {}
// Diese statische Methode gibt die Instanz zurueck.
public static function getInstance() {
if (self::$instance === NULL) {
self::$instance = new self;
}
return self::$instance;
}
// Klonen per 'clone()' von außen verbieten.
private function __clone() {}
}
$singleton = Singleton::getInstance();
?>
Implementierung in Python (ab Version 2.2)
class Singleton(object):
def __new__(type, *args):
# Falls es noch keine Instanz dieser Klasse gibt, wird eine erstellt und in _the_instance abgelegt.
# Diese wird dann jedes mal zurückgegeben.
if not '_the_instance' in type.__dict__:
type._the_instance = object.__new__(type)
return type._the_instance
def __init__(self):
if not '_ready' in dir(self):
# Der Konstruktor wird bei jeder Instanziierung aufgerufen.
# Einmalige Dinge wie z. B. die Initialisierung von Klassenvariablen müssen also in diesen Block.
self._ready = True
Implementierung in Perl
Die Methode instance gibt $instance zurück, bzw. initialisiert es vorher noch, wenn dies noch nicht geschehen ist.
<code=perl> package My::Singleton;
use strict; use warnings;
my $instance;
sub instance() {
return $instance or $instance = bless {};
}
42; </code=perl>
Dies kann dann mit dem folgenden Code genutzt werden:
<code=perl> use My::Singleton;
my $eins = My::Singleton->instance; # eine neue Instanz my $zwei = My::Singleton->instance; # die gleiche Instanz
</code=perl>
Mit CPAN-Modul
Etwas erweiterte Funktionalität bietet das CPAN-Modul Class::Singleton.
Eager Creation
Zwar ist auch das oben gezeigte Beispiel sicher bezüglich Nebenläufigkeit, weil die zentrale Methode getInstance
mit dem Java-Schlüsselwort synchronized
markiert ist. Eine einfachere Alternative dazu stellt jedoch die Möglichkeit dar, das Einzelstück 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 Singleton
referenziert wird.[1] Da der Zugriff nur via getInstance()
erfolgt, ist dies auch der spätest mögliche Zeitpunkt und entspricht somit der Lazy Evaluation ohne den Synchronisierungs-Overhead.
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;
}
}
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
:
public final class Singleton {
/** Private Klasse, einzige Instanz von Singleton wird beim Laden von Holder erzeugt. */
private static class Holder {
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 Holder.INSTANCE;
}
}
Hierdurch können andere statische Methoden von Singleton
gerufen oder in dieser definierte Konstanten referenziert werden, ohne INSTANCE
bereits zu erzeugen.
Implementierung in C#
Eine threadsichere Methode entnommen aus dem MSDN-Mag[2].
using System ;
sealed class Singleton
{
private Singleton() { /* ...hier optional Initialisierungscode... */ }
public static readonly Singleton Instance = new Singleton() ;
}
// Zugriff über
Singleton s = Singleton.Instance ;
Das Singleton-Prinzip wird bei C# durch 2 Maßnahmen erreicht:
- Durch die Deklaration des Konstruktors als privat (private Singleton(){}) kann dieser von außerhalb der Klasse nicht mehr aufgerufen werden - das Erstellen des Objektes ist also nur noch von innerhalb der Klasse möglich.
- Das statische öffentliche Feld Instance speichert und liefert schreibgeschützt Zugriff auf das einzige Objekt der Klasse.
Das Schlüsselwort sealed teilt dem Compiler mit, dass die Klasse nicht abgeleitet werden darf. Durch den privaten Konstruktor könnten Ableitungen ohnehin nicht instanziiert werden, aber durch sealed kann der Compiler einige Optimierungen anwenden.
Wichtig: Der Konstruktor wird von der CLR nicht automatisch beim Programmstart aufgerufen. Erst beim ersten Zugriff auf eine Klasse werden alle statischen Member der Klasse initialisiert – in diesem Fall die Instanziierung des einen Objektes. Wenn also nie auf diese Klasse zugegriffen wird, wird der Konstruktor auch niemals ausgeführt – und das Objekt wird nie erstellt. Dieser Effekt tritt auch dann noch ein, wenn die Klasse selbst als statisch deklariert wird.
Möchte man sicher gehen, dass das Singleton-Objekt beim Programmstart erstellt wird, so muss man einen beliebigen Zugriff in das Hauptprogramm ("public static void Main() { }") einbauen.
Varianten
- Wenn man seine Klasse wie in o. g. Beispiel nicht von einer anderen Klasse ableiten muss, kann man die Klasse auch einfach als statisch deklarieren – das entspricht immer noch dem Singleton-Prinzip. Das o. g. Beispiel ist eigentlich nur ein Workaround für die Tatsache, dass man in C# statische Klassen nicht von anderen Klassen ableiten kann.
- Möchte man eine bestimmte Anzahl Instanzen dieser Klasse (Multiton), so kann man einfach weitere statische öffentliche Instanzfelder hinzufügen ("public static readonly Singleton Instance2 = new Singleton() ;").
Implementierung als Generic 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:
public class Singleton<T>
where T : new()
{
public static readonly T Instance=new T();
}
Ein konkreter Singleton würde dann in etwa so aussehen:
class MeinSingleton : Singleton<MeinSingleton>
{
}
Der Zugriff erfolgt hier über:
MeinSingleton.Instance
Implementierung in Perl
Hier wird $instance bereits bei der Initialisierung von Singleton angelegt. Durch das :shared wird das Beispiel außerdem thead-safe.
<code=perl> package Singleton;
use strict; use warnings;
my $instance :shared = bless {};
sub instance() { return $instance; }
42;
</code=perl>
Dieser Beitrag stammt in seiner ersten oder einer späteren Version der deutschsprachigen Wikipedia. Er ist dort unter Einzelstück_(Entwurfsmuster) zu finden, die Liste der bisherigen Autoren befindet sich in der Versionsliste. |