Models: Sinnvolles Einsatzgebiet von Singletons entdeckt25 Feb

Von Julian am 25.02.2010. Es wurde noch kein Kommentar hinterlassen. Du kannst einen Kommentar hinterlassen oder trackbacken.

Im Web findet man haufenweise Diskussionen um den Sinn von Singletons. Auf Wikipedia finden sich ausführliche Erklärungen zu Singletons. Dabei sind auch haufenweise Nachteile aufgelistet, beispielsweise der Hang zur Erschaffung von Äquivalenten zu globalen Variablen – eben Klassen, die überall zur Verfügung stehen.
Bisher hatte ich auch nur einige wenige sinnvolle Einsatzgebiete entdeckt. In einem ehemaligen Blog habe ich das Singleton-Pattern an Hand einer Klasse beschrieben, die sich ähnlich wie Konstanten verhält, aber viel mehr Speichermöglichkeiten (Arrays, Objekte, ..) bietet. Dadurch ließen sich Konfigurationen ermöglichen, die von einzelnen Komponenten beispielsweise im Programmdurchlauf nicht manipuliert werden dürfen.
Nun bin ich aber während Arbeiten an einigen Komponenten eines aktuellen Projektes auf ein neues Einsatzgebiet gestoßen, das sich sogar nur umständlich ohne Singletons umsetzen lässt: Repositories im Model-Aspekt des MVC-Patterns.

Repositories

Die Idee der Repositories habe ich mir grundsätzlich von flow3.typo3.org abgeschaut. Im Punkt 5.3 werden Repositories erklärt. Dabei geht es um die Repräsentation einer Menge von Objekten bzw. nicht irgendeiner Menge, sondern eigentlich der gesamten Menge. Beispielsweise existiert eine Model-Klasse “User”, die einen Benutzer mit all seinen Eigenschaften repräsentiert. Nun möchte man aber auch neue Benutzer anlegen. Zwar kann man dazu einfach ein neues Objekt anlegen, aber dieses implementiert eigentlich keine Methode, um sich selbst zu speichern – im Falle einer Datenbank. Das ist die Aufgabe eines User-Repositories. Über das User-Repository erhält man eigentlich erst Zugriff auf bestehende User-Objekte. Über die User-Objekte kann man wiederum die Attribute ändern.

Singleton-Einsatz

In meiner Vorgehensweise stellt ein User-Objekt also genau einen User dar. Das ist ein Element einer Menge – mathematisch betrachtet. Darüber steht das User-Repository, das die Menge an sich repräsentiert. Legt man einen neuen Benutzer an, instanziert man ein neues User-Objekt und fügt es der Menge hinzu. Nun ist es aber so, dass ich keinerlei gleichartige Mengen mehr haben möchte in meiner Anwendung. Das heißt, es soll per Definition nur ein User-Repository geben, das im Falle einer Datenbank eben die gesamte Benutzer-Tabelle als Ganzes repräsentiert und Lösch- oder Erstell-Operationen durchführt. Das User-Objekt dagegen führt nur Update- oder Select-Operationen aus.
Das Problem bei einem Repository als gewöhnliche Klasse ist, dass es mehrfach instanzierbar wäre. Daher habe ich zunächst statisch gearbeitet. Dann kam aber ein neues Problem hinzu: Ich möchte möglichst lazy-loading unterstützen, d.h. möglichst erst dann Daten aus der Datenbank lesen, wenn es wirklich nötig (also gefragt) ist. Daher speichere ich die Daten in akkurater Art und Weise in der Klasse, sobald ich sie einmal geladen habe und gebe diese beim nächsten Nachfragen wieder zurück. Dazu habe ich ein SplObjectStorage verwendet, um über User-Objekte iterieren zu können. Lustigerweise kann man dieses Attribut der Klasse aber nicht beim Deklarieren initialisieren. Demnach bräuchte ich einen Konstruktor. Im statischen Kontext kann man dies nur umgehen, indem man eine Initialisierung-Methode schreibt, die bei jedem statischen Methodenaufruf geprüft wird, ob sie schon einmal aufgerufen wurde.
Beispiel:

<?php
class UserRepository {
	private $initialized = FALSE;
	private $users;
 
	static public initialize(){
		if(!self::$initialized){
			self::$users = new SplObjectStorage()
			//do something
		}
	}
 
	static public foo(){
		self::initialize()
		//do something
	}
}
?>

Es muss also bei jeder beliebigen statischen Methode ein Aufruf von initialize() durchgeführt werden, um self::$users zu initialisieren. Ein heftiger Umstand, den ich so nicht hinnehmen wollte. Also fällt der statische Kontext weg, wenn ich schließlich einen Konstruktor haben möchte. Was tun? Einerseits soll das (Repository)Model von überall erreichbar und einmalig sein, andererseits möchte ich non-static context und einen Konstruktor.
Eine minimalst abgeänderte Version eines Singletons macht es möglich:

<?php
 
abstract class Singleton {
	static private $instance = null;
 
	final private function __construct(){}
	final private function __clone(){}
 
	final static public function getInstance(){
		if (null === self::$instance) {
			self::$instance = new self;
			self::$instance->initialize();
		}
		return self::$instance;
	}
 
	private function initialize(){}
}
 
?>

Beim Vererben dieser abstrakten Klasse wird sichergestellt, dass die Child-class ein Singleton ist und über die Möglichkeit verfügt, eine initialisierende Methode zu besitzen (initialize()).
Alle weiteren Methoden sind nun einfach ohne entsprechendes static-Schlüsselwort anzulegen und Zugriff auf die Klasse erhält man von überall mittels

$userRepository = UserRepository::getInstance();
$userRepository->doSomething();

Wer andere (oder bessere!) Ideen hat, der ist eingeladen jederzeit einen Kommentar zu hinterlassen.

Hinterlasse einen Kommentar

© 2009 GlobalIndustry-Project Blog