Moin Narses,
Wie überall im Leben gibt es hier stark verschiedene Meinungen zu. Viele betrachten das Singleton-Pattern eher als ein
Anti-Pattern . Trotzdem ist Singleton an sich ein doch eher verbreitetes Pattern, wobei ich damit eher in einem Softwarearchäologischen Kontext damit Erfahrung gemacht habe, die nämlich im wesentlichen darin besteht, ein Singleton irgendwo wieder herauszupulen.
Mein größtes Problem mit dem Singleton-Pattern ist, dass man sich überall und an jeder Stelle
tightly an das Singleton coupled (#buzzwordBingo). Ich liefere mal ein Beispiel (bin leider nicht fließend genug in PHP, aber das sollte ja keine Rolle spielen...):
C#-Quelltext
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:
| public class Singleton { private static Singleton instance;
private Singleton() {}
public static Singleton Instance { get { if (instance == null) { instance = new Singleton(); } return instance; } } }
public class MyCoupledClass { public void DoSomethingWithTheSingleton() { Singleton.Instance.DoSth(); } }; |
Durch die Verwendung von
Singleton.Instance (analog natürlich
Singleton::Instance() in PHP), führt man sich eine Abhängigkeit zwischen
MyCoupledClass und
Singleton ein, die man so ohne weiteres nicht erkennen kann - sie ist nämlich nicht wirklich zu sehen, ohne in den Funktionsbody zu schauen. Möchte ich jetzt in Zukunft eins der folgenden Szenarien abdecken:
- Ich benötige doch plötzlich mehrere Objekte vom Typ Singleton
- Ich möchte meiner Anwendung Unit-Tests hinzufügen
- Ich möchte Polymorphie nutzen und unter gewissen Bedingungen (d.h. nicht immer) eine andere Implementierung von Singleton zur Verfügung stellen
habe ich den Salat und muss potentiell viele Zeilen Code bearbeiten. Das ist teuer und macht zudem noch keinen Spaß
.
Besser ist es daher meiner Meinung nach, eigentlich für jeden Fall eine Form der
Dependency-Injection zu verwenden. Auch hier kann man den gleichen Effekt erreichen, wie mit einem Singleton (nämlich, dass nur eine Instanz einer Klasse existieren kann), jedoch lässt sich dieses Pattern innerhalb kürzester Zeit umbauen, sodass die o.g. Anforderungen erfüllbar sind. Ich stelle hier einmal
Constructor Injection vor, die eine von eine
Factory generierte, einzigartige Instanz einer Klasse in eine Abhängige Klasse injiziert, vor (#buzzwordBingo again
).
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: 40: 41: 42: 43: 44:
| public interface IMyInterface {
};
public class MyClass : IMyInterface { public MyClass(){}; };
public class Factory { private MyClass _myInstance; public Factory() { _myInstance = new MyClass(); }
public IMyInterface GetMyInterface() { return _myInstance; } }
public class MyDependantClass { private IMyInterface _iNeedThis; public MyDependantClass(IMyInterface dependency) { _iNeedThis = dependency; } }
public void Main() { var f = new Factory(); var m = new MyDependantClass(f.GetMyInterface()); var m2 = new MyDependantClass(f.GetMyInterface()); } |
Natürlich ist dieses Beispiel noch nicht das höchste der Gefühle, da hier noch unnötige Abhängigkeiten von der Factory bestehen und man zur Änderung der Implementierung hier die Factory anfassen muss. Durch geschickte Implementierung lässt sich hier noch eine Abstraktionsschicht einführen - dafür empfiehlt es sich aber, mal den Code oder zumindest die API eines
Dependency-Injection Frameworks anzuschauen. Trotzdem wird im wesentlichen das erreicht, was ein Singleton kann. Trotzdem kann ich (zum Beispiel für Tests) die Implementierung des
IMyInterface, welches für
MyDependantClass genutzt wird, an genau der Stelle ändern, an der ich die Klasse erzeuge.
Ich hoffe, ich konnte etwas helfen.
Gruß
Finn