PHP Entwurfsmuster :: Singleton 2
Vor kurzem haben wir das Entwurfsmuster Singleton an einem PHP Beispiel vorgestellt. Das Singletonmuster gewährleistet, dass immer nur eine Instanz einer Klasse zur Laufzeit existiert. Wir hatten als Beispiel ein Datenbankobjekt genannt, welches eine Datenbankverbindung kapselt und sinnvollerweise nicht jedes mal im Lauf des Scriptes neu instanziert werden sollte.
Singleton wäre genau das richtige für mein Problem, aber ich brauche 2 oder mehrere verschiedene Singleton Instanzen!
Probleme könnten jetzt auftreten, wenn ein Script zum Beispiel zwei unterschiedliche Datenbanken benötigt. Man könnte das Problem lösen, indem man für die zweite Datenbank eine eigene neue Klasse schreibt, welche wieder dem Singleton Muster folgt. Das ist aber unnötig, denn es reicht das Singleton in eine Variante abzuwandeln. Denn, was wir erreichen wollen ist, dass zur Laufzeit von der Datenbankklasse zwei Objekte instanziert werden können, und zwar zur Kapselung von zwei unterschiedlichen Datenbankverbindungen. Allerdings soll nach wie vor die Möglichkeit bestehen, nach dem einmaligen Instanzieren der beiden Objekte per getSingletonInstance() das ständige neu erzeugen dieser Objekte zu umgehen.
Dies kann erreicht werden, in dem die Instanz der Klasse nicht in einer statischen Variable $singletonInstance gehalten wird sondern in einem statischen Array $singletonInstances. die Methode getSingletonInstance() bekommt einen Parameter, der auch optional sein kann. Nehmen wir mal an das Script benötigt meistens eine Verbindung zur Datenbank ’standard’ und nur manchmal eine Verbindung nach ‘aussnahme’. Dann kann man über getSingletonInstance(’standard’) oder getSingletonInstance(‘aussnahme’) den vollen Vorteil eines Singletons nutzen, und hat nur Minimale Änderungen in der bisherigen Klasse vorgenommen. Man kann somit also Objekte anhand einer ID (wie z.B. ein Datenbankname) nur einmal pro Laufzeit und pro ID erzeugen.
Hier der geänderte Singleton Code in der Variante mehrere Objekte der Klasse zu erhalten anhand einer ID:
<?php class MySQLiException extends Exception { } class MySQLiConnectionException extends MySQLiException { } class iDatabaseObject extends MySQLi { private static $singletonInstances = array(); private function __construct($server, $user, $password, $database) { parent::__construct($server, $user, $password, $database); if (mysqli_connect_errno()) { throw new MySQLiConnectionException(mysqli_connect_error(), mysqli_connect_errno()); } } public static function getSingletonInstance($database = 'standard') { if(!isset(self::$singletonInstances[$database])) { if($database === 'standard') { self::$singletonInstances[$database] = new iDatabaseObject('server', 'user', 'password', 'standard'); } elseif($database === 'aussnahme') { self::$singletonInstances[$database] = new iDatabaseObject('server', 'user', 'password', 'aussnahme'); } else { throw new MySQLiConnectionException('No connection Data given for database "'.$database.'"'); } } return self::$singletonInstances[$database]; } public function __clone() { } public function __destruct() { $this->close(); } public function query($query) { return $result = parent::query($query); } public function getResultRowByID($ID,$query) { $result = array(); $stmt = $this->prepare($query); $stmt->bind_param("i", $ID); $stmt->execute(); $stmt->bind_result($result); $stmt->fetch(); $stmt->close(); return $result; } } ?>
Nun kann man im Code über iDatabaseObject::getSingletonInstance() auf die standard Datenbank und per iDatabaseObject::getSingletonInstance(‘aussnahme’) auf die Andere zugreifen. Natürlich wären so nun beliebig viele Verbindungen möglich.
Ausser für Datenbanken bietet sich diese Singleton Variante für die unterschiedlichsten Anwendungsbereiche an. Zum Beispiel könnte man eine User Klasse, welche User Daten eines Portals kapselt so entwerfen, dass sie über iUserObject::getSingletonInstance($userID) entweder ein neues Objekt oder ein bereits vorher in einer anderen Klasse instanziertes Objekt zurückgibt. Jeder User würde dann zur Laufzeit nur einmal ausgelesen werden, statt jedesmal wenn auf der Seite etwas von dem User angezeigt werden soll(z.B. in einem Kommentar der Name, Homepage und Bild)
Immer dann, wenn es Sinn macht, dass es ein Objekt einer Klasse zur Laufzeit nur einmal gibt setzt man also das Singleton ein. Braucht man zur Laufzeit zwar oftmals mehrere Objekte einer Klasse, aber kann diese Objekte einer ID zuordnen macht es Sinn das Singleton so abzuwandeln. Vor allem dann, wenn man weis, das zur Laufzeit dieses Objekt der Klasse ständig gebraucht wird.
Abzuraten ist davon, wenn es zwar möglich ist, dass es einen zweiten oder dritten Zugriff auf die gleichen Daten geben könnte, es in der Regel aber nicht der Fall ist, denn dann würde man sehr viel unnötigen Speicher belegen, da wäre es sinnvoller dann tatsächlich dieses Objekt im Lauf des Scripts dann mehrmals zu erstellen oder eine Art manuelles Objektcaching einzusetzen.
Weiterführende Literatur:
Tags: entwurfsmuster, MySQLi, PHP, singleton

Hi,
schon mal was von ‘grants’ gehört? wenn ich eine verbindung zum db-server habe, dann kann ich einem nutzer auf dem server bestimmte rechte geben auf welche schemen (datenbanken) er zugreifen darf. somit brauche ich nur eine verbindung. sofern es sich um ein un die selbe db handelt. wenn nicht lohnt es sich eh eine neue klasse zu schreiben…