3. Januar 2012 | 1 Comment Allem voran möchte ich euch allen ein Frohes Neues Jahr wünschen! Ich habe mir für diesen und die kommenden Postings eine kleine Serie überlegt: Design Patterns in der Praxis. Im ersten Teil möchte ich mit einem der „bekanntesten“ Design Patterns (oder auch „Entwurfsmuster„) beginnen: Dem Singleton. Zuerst: Was ist das Singleton und was kann es? Bei Objekt-Orientierten Programmiersprachen ist es über den „new“ Operator möglich beliebig viele Instanzen einer Klasse zu erstellen. Aber es gibt auch Situationen in denen wir das nicht erlauben möchten: Stellen wir uns vor, wir haben eine Klasse die für das cachen (oder „Zwischenspeichern“) von Daten verantwortlich ist. Diese Klasse soll es nur ein einziges mal geben, denn schließlich möchten wir nicht zwingend zu jeder instanziierung eine neue Cache-Datei erzeugen! Genau dafür gibt es das „Singleton“. Das Singleton definiert den Konstruktor einer Klasse als privat (private). Ebenso die magische __clone Methode. Zum instanziieren eines Singleton gibt es üblicherweise eine „getInstance()“ Methode, welche öffentlich (public) und statisch ist. Hier ein kurzer Code-Ausschnitt wie sich die instanziierung zwischen normalen Klassen und Singleton-Klassen unterscheidet: // Diese Klasse lässt sich problemlos mehrfach erstellen und klonen. $class = new Klasse(); $class2 = new Klasse(); $class3 = clone $class2; // $class und $class2 beinhalten seperate Instanzen unserer Klasse. // Das Singleton lässt sich nur so aufrufen: $singleton = Single::getInstance(); $singleton2 = Single::getInstance(); // $singleton und $singleton2 beinhaltet die selbe instanz der Klasse "Single" Natürlich möchte ich euch nicht vorenthalten wie eine Singleton Klasse aufgebaut sein muss: // Die Singleton-Klasse final class Singleton { /** * Diese Variable beinhaltet unsere einmalige Instanz. * @var Singleton */ private static $instance = NULL; /** * Der private Konstruktor sorgt dafür das die Klasse nur aus sich selbst heraus instanziiert werden kann. */ private function __construct() {} /** * Diese statische Methode gibt die Instanz zurueck. * * @return Singleton */ public static function getInstance() { if (NULL === self::$instance) { self::$instance = new self; } return self::$instance; } /** * Klonen per 'clone()' von außen verbieten. */ private function __clone() {} } (Singleton Klasse gefunden auf wikipedia) Das „final“ am Anfang der Klassendefinition gibt an, das diese Klasse nicht abgeleitet werden darf – Ich persönlich baue sowas nicht ein, doch bei größeren Projekten macht es sicherlich Sinn um den Fall vorzubeugen das Jemand die Klasse ableitet und z.B. einen öffentlichen Konstruktor einbaut. Da wir von der „getInstance()“ Methode direkt die Instanz bekommen, lässt sich hier eine Variable zum speichern der Instanz sparen – jedoch macht das nur Sinn, wenn man nur mal eben auf eine Methode zugreifen möchte (erinnert mich immer etwas an das „Fluent Interface„, zu dem ich in der Zukunft ebenfalls einen Eintrag schreiben möchte): // Sinnvoll, wenn dies die einzige Stelle ist für die wir die "Single" Klasse benötigen: $result = Single::getInstance()->getResult(); // Ebenfalls Sinnvoll: $singleton = Single::getInstance(); $result_a = $singleton->getResultA(); $result_b = $singleton->getResultB(); $result_c = $singleton->getResultC(); // Nicht sehr Sinnvoll: $result_a = Single::getInstance()->getResultA(); $result_b = Single::getInstance()->getResultB(); $result_c = Single::getInstance()->getResultC(); In welchen Bereichen macht ein Singleton eigentlich Sinn? Nun, die typischen Situationen in denen wir nur eine einzige Instanz einer Klasse benötigen oder erlauben möchten sind, wenn wir mit Datenbanken oder Sessions arbeiten. Natürlich gibt es noch viele viele andere Anwendungsmöglichkeiten, doch diese beiden sind sehr einfach zu verstehen 😉 Stellen wir uns vor wir haben eine Objekt-Orientierte Datenbankabstraktion – Wir möchten nicht für jede Model-Klasse eine eigene Datenbankverbindung aufbauen! Deshalb ist es nur von Vorteil wenn wir zwar mehrere Model-Klassen und Instanzen haben, aber nur eine einmalige „Datenbank“-Instanz welche unsere Verbindung, Fehler, Queries und Ergebnisse behandelt. So können wir außerdem sichergehen das nicht „aus versehen“ mehrere Verbindungen aufgebaut werden. Das angesprochene Beispiel „Session“ dürfte sich von selbst erklären: Zur Laufzeit kann immer nur eine einzige Session existieren – Entsprechend macht das Singleton an dieser Stelle Sinn. Nachteile des Singleton-Patterns! – Wie auf der Wikipedia Seite (link) richtig erläutert wird, ist man als Entwickler schnell dazu verleitet prozedural zu entwickeln. In einer Objekt-Orientierten Programmiersprache ist das ein deutlicher Rückschritt! Genau wie bei normalen Funktionen lässt sich das Singleton an jeder Stelle zu jedem Zweck aufrufen – Es kann also schnell passieren das wir Singleton Klassen als „Helper“ verwenden und immer mehr statischen Methoden entwickeln wenn es sich grade anbietet. – Singleton Klassen lassen sich nicht so einfach deallokieren. Das bedeutet, der Speicher der von einem Singleton benutzt wird, lässt sich nicht so einfach wieder freigeben wie der einer normalen Klasse (ein einfaches unset() reicht nicht!). Dadurch ist nicht immer gewiss wann der Destruktor einer Klasse, falls dieser existiert, aufgerufen wird – Speziell bei Log-Klassen ist das ein Nachteil, weil es unsicher ist wann die Log-Datei geschrieben und geschlossen wird (Da dies typischerweise im Destruktor passiert). – Singletons lassen sich (aufgrund des vorangegangenen Punktes) nicht so leicht testen wie andere Klassen. Zum Testen wird eine Klasse idealerweise immer wieder „frisch“ instanziiert, was jedoch beim Singleton ein Problem darstellt: // Wir erzeugen eine Singleton Klasse und weisen einer internen Variable einen Wert zu. $singleton = Single::getInstance(); $singleton->variable = 'Wert'; unset($singleton); // Trotz der Nutzung von unset() wird hierdurch der String "Wert" auf dem Bildschirm dargestellt. echo Single::getInstance()->variable; Als kleines Fazit möchte ich schreiben das Singleton Klassen durchaus sehr Sinnvoll sein können und uns Entwicklern oft die Arbeit erleichtern – Wir müssen nur aufpassen nicht in die prozedurale Entwicklung abzurutschen und uns möglichst vorher Gedanken machen ob ein Singleton zwingend erforderlich ist. An vielen Stellen können wir durch eine gute Planung unnötigen Aufwand verhindern und dazu beitragen das unser Projekt sauber und funktional bleibt. Hiermit beende ich meinen heutigen Eintrag und freue mich wie immer über eure Anregungen und Kommentare!