In einer kleinen Reihe von Blogbeiträgen möchte ich die nächste Zeit auf verschiedene Entwurfsmuster eingehen, die uns als Programmierer immer wieder begegnen und uns die Arbeit erleichtern.
Es geht los mit einem der einfacheren Muster: Dem Singleton. Ich will verdeutlichen wozu es verwendet wird, welche Probleme es bereiten kann und natürlich gibt es auch ein Codebeispiel. Außerdem werde ich noch auf das Multiton als Weiterführung der Singleton-Idee eingehen.
Beispiel und Erklärung
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
class MySingleton { private static $instance; private function __construct() { } private function __clone() { } /** * @return self */ public static function getInstance() { if (self::$instance === null) { // Erstelle eine neue Instanz, falls noch keine vorhanden ist. self::$instance = new self(); } // Liefere immer die selbe Instanz. return self::$instance; } } |
Ein Singleton ist sehr einfach an seinem nicht öffentlich zugänglichen Konstruktor zu erkennen. Wie der Name bereits ausdrückt, soll vom Singleton nur eine einzige Instanz während des gesamten Programmablaufs verwendet werden. Um dies sicherzustellen sind __construct()
und __clone()
als private
angelegt. Die Instanz bekommt der Anwender über die statische Methode getInstance()
.
Nutzen und Anwendungsbereiche
Ein Singleton bietet sich immer dann an, wenn eine Instanz für den Programmablauf ausreichend oder notwendig ist. Beispielsweise sollte nur eine Verbindung zur Datenbank aufgebaut und diese dann immer wieder verwendet werden.
Die Verwaltung der Dienste sollte wohl ebenfalls als Singleton ausgeführt werden, da sonst der komplette Programmablauf durcheinander gerät.
Beispiele
- Datenbankverbindung
- Logging
- Dependency Injection
Probleme und UnitTests
Genau genommen ist ein Singleton ein Anti-Pattern! Es ist nur durch Tricks testbar (Hier steht wie es trotzdem geht) und reduziert die Wartbarkeit des Programmcodes.
Da der einzige Weg an die Instanz zu kommen MySingleton::getInstance()
ist, entsteht bei jeder Verwendung eine harte Abhängigkeit, die, wenn das Singleton jemals getauscht werden muss, zum Problem werden kann.
Um Singletons zu vermeiden sollte stattdessen mit Dependency Injection gearbeitet werden.
Das Multiton
Beim Multiton geht es darum eine bestimmte Anzahl an Instanzen vorzuhalten. Beispielsweise könnte eine Anwendung zwei Datenbanken oder mehrere Logger benutzen.
Der Code sähe dann wie folgt 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 |
class MyMultiton { const INSTANCE_ONE = 'one'; const INSTANCE_TWO = 'two'; private static $instances = []; private function __construct() { } private function __clone() { } /** * @param $instanceName * * @return self */ public static function getInstance($instanceName) { if (!array_key_exists($instanceName, self::$instances)) { self::$instances[$instanceName] = new self(); } return self::$instances[$instanceName]; } } // Verwendung $inst = MyMultiton::getInstance(MyMultiton::INSTANCE_ONE); |
Die Konstanten für die Instanzen sollten natürlich ihrem Verwendungszweck entsprechend benannt werden.
Für ein Multiton gelten umso mehr die oben beschriebenen Probleme und die Verwendung wird nur selten sinnvoll und unumgänglich sein.
Weitere Entwurfsmuster findest du hier.