Was spricht gegen die Verwendung des Singleton-Patterns (ja, ich meine das GOF-Singleton) um einen globalen Zugangspunkt zu einem zustandslosen (!) Service zu bekommen?
“Zustandslos” heist, dass sich zwischen zwei Aufrufen kein Zustand “gemerkt” wird. Das heist aber nicht, dass der Service während der Bearbeitung der Anfrage keinen Zustand haben darf/kann. Und genau da entstehen Probleme, wenn der Dienst als Singelton bereit gestellt wird. Der einzige Singelton der IMHO tatsächlich sinnvoll ist ist das ServerSocket, das die Kontaktaufnahme organisiert und so schnell wie möglich einen eigenen Server-Thread für jede Anfrage auf macht.
bye
TT
Das ist richtig. Ich meine mit „zustandslos“, dass das Serviceobjekt keine Felder hat (außer ein statisches Feld, in dem die Instanz gespeichert ist). Methodenlokaler Zustand wäre also erlaubt.
*** Edit ***
Vielleicht ein Beispiel (ist jetzt kein echter Service, aber vom Prinzip her ähnlich): Was spricht dagegen, eine Factory als Singleton zu implementieren?
private static final MyFactory instance = new MyFactory();
private MyFactory() {}
public MyFactory getInstance() {
return instance;
}
public MyObject createMyObject() {
return new SpecialMyObject();
}
}```
Eigene Singletons sind immer schwerer als man denkt. Gerade, wenn versucht wird, Optimierungen vorzunehmen:
Zu Deinem Beispiel?
Warum muss es sich dabei um ein Singleton handeln? Warum instanziierst Du in diesem Fall nicht einfach normal eine Factory und rufst darauf die create-Methode auf? Was für einen Mehrwert versprichst Du Dir?
Ich persönlich finde Singleton (wenn nicht vom Container geliefert) in den meisten Fällen nicht sinnvoll.
Ein Punkt darin ist interessant: die Anmerkung mit mehreren Classloadern. Das führt bei zustandslosen Services allerdings auch nicht zu Problemen. Die weiteren Betrachtungen sind mir alle bekannt - lustig ist, dass die „beste“ Lösung, um ein statisches Feld lazy zu initialisieren, fehlt:
private Singleton() {}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
private static class SingeltonHolder {
static final Singleton instance = new Singleton();
}
}```
[quote=Sym;84246]Warum muss es sich dabei um ein Singleton handeln? Warum instanziierst Du in diesem Fall nicht einfach normal eine Factory und rufst darauf die create-Methode auf? Was für einen Mehrwert versprichst Du Dir?[/quote]
Wahrscheinlich hast du recht und es gibt einfach keine Vorteile von Singletons.
Mir geht es eben um Services, die von den Objekten im Domain-Layer benutzt werden. Und da die Objekte in diesem Layer nicht vom Container verwaltet werden, kann ich da schlecht den Service injizieren.
Services, Hilfsklassen, Factories etc. Es gibt einiges, bei dem man sicher sein kann, dass es wirklich nie nicht einen Zustand hat. Die Methoden könnten static sein, was aber nicht so schön ist, weil man dann nicht gegen Interfaces programmieren kann. Ich finde, Interface -> SingletonImpl einen gangbaren Weg. Und seit Java 5 mit Enums muss man auch dieses antiquierte GOF-Pattern nicht mehr verwenden. Eine Enum, die ein definiertes Interfaces implementiert und genau ein Element hat, wäre meine Wahl.
Ein Singleton verändert den Charakter wie ein Programm funktioniert.
class SingletonUser {
void foo() {
Singleton.getInstancd().doSomething();
}
}
kann nicht getestet werden ohne tatsächlich auf dem Singleton zu arbeiten, da das SingletonUser sich seine Abhängigkeiten selbst beschafft.
Ok. es geht mit den entsprechenden Frameworks, aber es ist nicht trivial.
Klassen, die von anderen Klassen (hier Service) erben, können schon mal keine Singletons sein:
private static HTTPService instance;
private HTTPService() {}
public static HTTPService getInstance() {
synchronized(HTTPService.class) {
if(instance == null) {
instance = new HTTPService();
}
return instance;
}
}
}```ist dann eine falsche Designentscheidung, wenn noch andere Dienste hinzukommen sollen. Besser wäre in diesem Fall eine ServiceFactory, diese erstellt dann bei Bedarf genau eine Instanz der Klasse HTTPService.
[QUOTE=cmrudolph]Wahrscheinlich hast du recht und es gibt einfach keine Vorteile von Singletons.
Mir geht es eben um Services, die von den Objekten im Domain-Layer benutzt werden. Und da die Objekte in diesem Layer nicht vom Container verwaltet werden, kann ich da schlecht den Service injizieren.[/QUOTE]
Das hängt ja ganz vom Container ab. CDI kann alles und jeden Injecten. Und wie hier schon benannt würde ich alleine der Testbarkeit wegen keine Singletons verwenden.
Das Argument trifft aber auch auf DI zu. Bei der Verwendung eines DI Frameworks ändert sich die Charakteristik noch viel entscheidender. Auch Factories wären dann tabu.
Das sehe ich also nicht als Gegenargument an.
Die hier mehrfach aufgeführte Testbarkeit sehe ich ein - das Argument zieht natürlich auch bei Factories.
Der Vorschlag mit den Enums ist sehr gut. Damit gibt es keines der Probleme, die hier bisher aufgeführt wurden. Sie sind mockbar (zumindest mit jMockit) und ich bin nicht vom DI Framework abhängig (ich nutze Spring, da schein es zwar auch möglich zu sein, in unmanaged beans zu injizieren, aber nur programmatisch).
[QUOTE=cmrudolph]Das Argument trifft aber auch auf DI zu. Bei der Verwendung eines DI Frameworks ändert sich die Charakteristik noch viel entscheidender. Auch Factories wären dann tabu.
Das sehe ich also nicht als Gegenargument an.[/QUOTE]
Das verstehe ich nicht. Inwiefern wird die Charakteristik geändert? Und warum sollten Factories tabu sein? Das wird doch in den meisten DI-Frameworks großartig unterstützt.
Die Aussage bezog sich darauf, dass sich die “Charakteristik” des Programmes ändert, weil ich statt z. B. einer injizierten Abhängigkeit ein Classname.getInstance() verwenden muss. Mein Gegenargument war, dass ich auch beim Verwenden eine DI Frameworks die “Charakteristik” des Programmes ändere, im Gegensatz zu einem Programm, bei dem ich kein Framework verwende. Es ist einfach eine Entscheidung, die ich treffen muss und daher kein Argument gegen Singletons.
OT: Zum Thema Spring und Injection in unmanaged beans habe ich einen interessanten Artikel gefunden: http://www.javacodegeeks.com/2011/02/domain-driven-design-spring-aspectj.html
Wenn man AspectJ LoadTimeWeaving aktiviert, kann man auch in unmanaged beans injizieren. Das ganze dann natürlich auf die aktuelle Spring Version angepasst: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-aj-ltw
Bei DI aendert sich der Charakter aber zum positiven
Die Strukturen koennen besser getrennt und testbar gemacht werden, etc. pp.
Nebenbei, DI und Factories beissen sich gar nicht.
Ansonsten sind Factories sehr gut testbar wenn die Struktur passt, zB., wenn man DI verwendet
So ist es - ich hatte ja auch keine Beurteilung abgegeben, sondern nur das Argument nicht als solches akzeptiert.
In dem Sinne, dass man dann Factories injiziert?
Ja, zum Beispiel.
Bei DDD ist es sehr ueblich Factories zu haben anstatt alles per reiner DI/AOP Magie zu erzeugen.
Damit weiss man dann sofort, wohin man sich wenden moechte wenn man eine Instanz braucht bzw. wenn mna etwas anpassen muss.
Im Domaenenmodell macht sich das IME auch sehr gut, die Factories haben dabei genau definierte Aufgaben, eben Instanzen zu erzeugen
wenn man Singletons hat, dann wird man sie auch über Indirektion verstecken,
ob von außen rein oder selber irgendwo nachgefragt:
getConfig().getXY(),
dann kann man für Testen (edit: oder bei Programmerweiterung) genauso irgendwas anderes zurückgeben
die Indirektion ist der entscheidene Angriffspunkt für Austauschbarkeit,
ob im Code ein statischer Zugriff oder Konstruktoraufruf einer bestimmten Klasse X steht ist gleich schlecht zu testen/ edit: wie für alles andere
ganz ohne irgendwelchen Zustand ist Singleton wirklich etwas fraglich,
lohnt sich eher bei exklusiver Kontrolle über gewisse Daten oder aufwendige Lade-Prozesse, auch Cache
Durchaus. Für Guice gibt es hierfür z.B. die Erweiterung AssistedInject.
Man kann Factories als auch die direkt erzeugten Beans injizieren. Beides ist angenehm und testbar.
Durch „Domain-Driven Design: Tackling Complexity in the Heart of Software“ bin ich überhaupt erst darauf gekommen. Mir ist bewusst, dass das Buch vor über 10 Jahren verfasst wurde und DI zu dem Zeitpunkt noch nicht so verbreitet war, wie heute. Allerdings erschien mir in dem Kontext (nämlich dünne Services im Domain-Layer) ein Singleton durchaus als angebracht und habe deshalb die Diskussion hier gestartet.
Bei DDD fühlt sich der Code für mich schön/elegant an (ja, klingt ein bisschen esoterisch, aber bei mir hat Programmieren viel mit Gespür zu tun). Ich bin mit dem Buch zwar noch nicht so weit, aber es ist sehr vielversprechend.
Alles was dann wieder konkreter wird, wirft dann vielleicht nochmal neue Fragen auf. Derzeit ist auf jeden Fall erst einmal Refactoring angesagt, um einiges wieder gerade zu ziehen, wo ich Kompromisse eingegangen bin.
[quote=SlaterB]wenn man Singletons hat, dann wird man sie auch über Indirektion verstecken,
ob von außen rein oder selber irgendwo nachgefragt:
getConfig().getXY(),
dann kann man für Testen (edit: oder bei Programmerweiterung) genauso irgendwas anderes zurückgeben[/quote]
Das Problem: Dadurch dass das binding statisch ist, muss man entweder tricksen (bestimmte Mock L:ibraries bieten das), oder man muss einen sog. “Test Hook” in dem prod. Code des Singletons packen.
Beides schlecht da entweder der Test komplexer wird durch spezielle Mocklibraries die man sonst gar nicht braeuchte, im zweiten Falle hat man dann Prod. Code er sich auch um die Tests kuemmert.
Mit reinem DI braucht da nicht mal was zu tricksen, der Test wird viel einfacher und sauberer, der Prod Code auch, man denke nur an dieses unsaegliche static INSTANCE etc. pp.