Entwickler-Ecke
Programmiersprachen (Server) - Statische Klassen vs. Singleton-Pattern - Konzeptfragen
FinnO - So 01.11.15 19:10
Moin Narses,
Wie überall im Leben gibt es hier stark verschiedene Meinungen zu. Viele betrachten das Singleton-Pattern eher als ein
Anti-Pattern [
http://stackoverflow.com/questions/12755539/why-is-singleton-considered-an-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 [
http://stackoverflow.com/questions/2832017/what-is-the-difference-between-loose-coupling-and-tight-coupling-in-object-orien] (#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 [
https://de.wikipedia.org/wiki/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 ;)).
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: 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 [
http://pimple.sensiolabs.org/] 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
Palladin007 - So 01.11.15 21:55
Singleton ist ganz sicher ein gefährliches Pattern und so wie ich das sehe auch ein Anti-Pattern, allerdings nicht unnütz.
Wichtig ist nur, dass man sich bei der Anwendung sehr genau überlegt, ob es tatsächlich notwendig ist und sich nicht anders lösen lässt.
In den meisten Fällen lässt sich das auch über eine Factory lösen und die Nachteile vom Singleton sind dann gelöst.
Manchmal nutze ich aber auch beides Zusammen: Ein Singleton für die Factory.
Sinnvoll ist das dann, wenn die Factory diverse Vorbereitungen treffen muss und dabei sehr teure Objekte erzeugt und gecached werden.
Ein solcher Vorgang ist in den meisten Fällen nur einmal notwendig.
Wir haben in der Firma z.B. einige Bereiche, die sehr viel mit Reflection oder sogar mit Reflection.Emit arbeiten müssen. Da geht viel Zeit flöten, was es zu vermeiden gilt, daher werden die aufwendigen Parts beim Anwendungsstart in einen Thread geschoben, damit sie dort in Ruhe vor sich hin laufen können, bis sie dann gebraucht werden. Die Vorbereitungen sind dann als Singleton erreichbar.
Das Ergebnis: Der sehr zeitaufwendige Ablauf ist überhaubt nicht spürbar und stellt damit eine enorme Performance-Verbesserung dar.
Ich nutze auch gerne ein Singleton für eine Factory, wenn diese Factory das Dependency-Injection-Pattern umsetzen soll ^^
Die lädt dann einmal am Anfang die ganzen Abhängigkeiten und hält sie dann bereit, bis sie gebraucht werden.
Ein anderes Beispiel für ein Singleton:
Eine global in der Anwendung existierende Datenbank-Verbindung.
Das Öffnen und Schließen ist teuer und wenn sehr häufig Datenbank-Interaktionen notwendig sind, dann könnte es sinnvoll sein, diese Verbindung hinter einem Singleton zu halten.
Oder wenn dem User ein Anmelde-Dialog angezeigt werden soll, dann sollte das einmalig und die Session im Hintergrund aktiv bleiben.
Dazu noch zwei Methoden Open und Close, Open wird beim Starten aufgerufen, Close beim Beenden der Anwendung.
Das Singleton sollte die letzte Wahl sein, wenn z.B. eine Factory nicht aushelfen kann.
Aber vergesst dabei nicht: Andere Patterns haben auch ihre Nachteile, die vielleicht in dem konkreten Fall schwerer wiegen.
Die Factory z.B. stellt eine ebenso starke Abhängigkeit dar, wie das Singleton.
FinnO - Mo 02.11.15 22:51
Palladin007 hat folgendes geschrieben : |
Die Factory z.B. stellt eine ebenso starke Abhängigkeit dar, wie das Singleton. |
Ein Stück weit würde ich hier schon noch widersprechen wollen. Der elementare Unterschied ist nämlich, dass man sich durch das Singleton in der Regel eine Abhängigkeit von einer statischen Methode hereinholt (die wie gesagt nur im Funktionsbody sichtbar ist), während man die Factory geschickt nutzen kann um die Instanz der gewünschten Klasse in die abhängige Klasse zu injizieren. So findet eine
inversion of control* ('tschuldigung, aber ich glaube man kann das wirklich nicht auf deutsch sagen) statt und die abhängige Klasse muss die tatsächliche Implementierung der Abhängigkeit definitiv nicht kennen.
*) Das heißt, die Konstruktion der Abhängigkeit erfolgt außerhalb der Klasse.
Klar hat man noch an irgendeiner Stelle die Factory stehen, aber das zu vermeiden wird dann irgendwann auch utopisch - zudem ist die Factory selbst wiederum als Interface oder Generic abstrahierbar und ggf. durch eine andere Implementierung ersetzbar, mockbar oder was auch immer.
Gruß
Finn
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2024 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!