|
Jun
05
|
Mit dem Singleton Pattern kann man bei der Implementierung von Anwendungen sicher stellen, dass von einer spezifischen Klasse immer nur genau eine Instanz vorhanden ist. Nehmen wir in unserem Beispiel einen Datenbankwrapper. In der Regel arbeiten wir mit _einer_ Datenbank und haben auch nur _eine_ Verbindung zu ihr. Dazu verwenden wir in unserem Beispiel einen Wrapper (natürlich ohne Funktionalität). Wir möchten also, dass von diesem Wrapper immer nur eine Instanz zur Verfügung steht, damit wir von jeder Stelle des Codes aus auf die selbe Instanz zugreifen können.
Schauen wir uns dazu einmal das UML-Diagramm an:
Wir definieren zunächst ein Interface, welches die Schnittstelle getInstance definiert. Diese gibt dann die aktuelle Instanz zurück. Anders als bei der “normalen” Implementation von Klassen ist der Konstruktor protected, kann also nicht von außen aufgerufen werden, das heißt, dass die Klasse nicht mit dem new-Operator instanziiert werden kann. Zusätzlich muss die Methode __clone noch protected deklariert werden, damit man durch den Einsatz des clone-Operators keine neuen Instanzen erzeugen kann.
Die Implementation der Klassse sieht so aus:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | interface Singleton { public static function getInstance (); } class DbWrapper implements Singleton { protected static $Instance = NULL; protected function __construct() { } protected function __clone() { } public static function getInstance() { if ( self::$Instance === NULL ) { self::$Instance = new self; } return self::$Instance; } } |
Wie wir sehen prüft die Methode getInstance beim Aufruf, ob bereits eine Instanz der Klasse in der statischen Klassenvariable $Instance vorhanden ist und legt sie bei Bedarf vor dem zurückgeben an.
Nun wollen wir schauen, ob unser Vorhaben erfolgreich war und rufen folgendes Testscript auf:
1 2 3 4 5 6 7 8 9 10 11 | $SingleWrapperA = DbWrapper::getInstance(); $SingleWrapperB = DbWrapper::getInstance(); if( $SingleWrapperA == $SingleWrapperB ) { echo 'Wrapper A und Wrapper B sind gleich<br />'; } else { echo 'Wrapper A und Wrapper B sind NICHT gleich<br />'; } |
Die Ausgabe im Browser zeigt uns, dass wir alles richtig gemacht haben und das Anlegen von mehreren Instanzen nicht mehr möglich ist:
Wrapper A und Wrapper B sind gleich
Jetzt könnte es aber nötig sein verschiedene Datenbanktypen zu verwenden und hier jedoch auch nur jeweils eine Instanz zu haben. Schauen wir uns die Veränderung im UML an:

Wie wir sehen übernimmt getInstance nun einen Namen und speichert die Instanz nicht mehr in einer reinen Variable ab, sondern in einem Array, welches die jeweilige Instanz über den Namen zuordnet (alles sehr rudimentär
).
Die Implementation dafür sähe folgendermaßen aus:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | interface SingletonMulti { public static function getInstance ( $name ); } class DbWrapperMulti implements SingletonMulti { protected static $Instance = array(); protected $name; protected function __construct() { } protected function __clone() { } public static function getInstance( $name ) { if ( !self::$Instance [ $name ] ) { self::$Instance [ $name ] = new self; self::$Instance [ $name ]->setName ( $name ); } return self::$Instance [ $name ]; } public function setName ( $name ) { $this->name = $name; } } |
Wir speichern also die entsprechenden Instanzen in einem Array und weisen ihnen darüber hinaus einen Namen zu (ohne diesen wären die Instanzen aller Typen gleich, da es sich um leere Instanzen handeln würde).
Folgendes Script verwenden wir nun, um unsere Klasse zu testen:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | $SingleWrapperMySql_A = DbWrapperMulti::getInstance( 'mysql' ); $SingleWrapperMySql_B = DbWrapperMulti::getInstance( 'mysql' ); $SingleWrapperPgSql_A = DbWrapperMulti::getInstance( 'pgsql' ); $SingleWrapperPgSql_B = DbWrapperMulti::getInstance( 'pgsql' ); if( $SingleWrapperMySql_A == $SingleWrapperMySql_B ) { echo 'MySql A und MySql B sind gleich<br />'; } else { echo 'MySql A und MySql B sind NICHT gleich<br />'; } if( $SingleWrapperPgSql_A == $SingleWrapperPgSql_A ) { echo 'PgSql A und PgSql B sind gleich<br />'; } else { echo 'PgSql A und PgSql B sind NICHT gleich<br />'; } if( $SingleWrapperPgSql_A == $SingleWrapperMySql_A ) { echo 'PgSql und MySql sind gleich<br />'; } else { echo 'PgSql und MySql sind NICHT gleich<br />'; } |
Die Ausgabe im Browser zeigt uns, dass die beiden MySql und PgSql Wrapper jeweils gleich sind, jedoch der MySql und der PgSql Wrapper unterschiedlich sind.
MySql A und MySql B sind gleich
PgSql A und PgSql B sind gleich
PgSql und MySql sind NICHT gleich
Somit haben wir unser Ziel erreicht mehrere Typen des Wrappers jeweils einmal zu instanziieren.
Juni 7th, 2007 at 14:42
Wenn man sich eingehender mit MVC beschäftigt stellt man fest, dass es ziemlich schwierig ist die Schichten so anzulegen, dass sie vollständig von einander entkoppelt sind. Ein Knackpunkt dabei ist die Datenübertragung. Wie teilt die Business Logic der Pr