Was ist so schlimm an Singletons? [geschlossen]


1983

Das Singleton-Muster ist ein voll bezahltes Mitglied des Musterbuchs des GoF , scheint aber in letzter Zeit von der Entwicklerwelt eher verwaist zu sein. Ich verwende immer noch ziemlich viele Singletons, insbesondere für Factory-Klassen , und obwohl Sie bei Multithreading-Problemen (wie bei jeder Klasse tatsächlich) etwas vorsichtig sein müssen, verstehe ich nicht, warum sie so schrecklich sind.

Insbesondere der Stapelüberlauf scheint davon auszugehen, dass alle der Meinung sind, dass Singletons böse sind. Warum?

Bitte unterstützen Sie Ihre Antworten mit " Fakten, Referenzen oder spezifischem Fachwissen ".


6
Ich muss sagen, dass mich die Verwendung eines Singleton-Designs in letzter Zeit verbrannt hat, als ich versucht habe, den Code anzupassen. Da ich es in meiner Freizeit mache, bin ich fast zu faul, um dies umzugestalten. Schlechte Nachrichten für die Produktivität.
Marcin

71
Es gibt viele 'Nachteile' in den Antworten, aber ich würde auch gerne einige gute Beispiele dafür sehen, wann das Muster gut ist, im Gegensatz zu den schlechten ...
DGM

50
Ich habe vor ein paar Monaten einen Blog-Beitrag zu diesem Thema geschrieben: jalf.dk/blog/2010/03/… - und lassen Sie es mich einfach direkt sagen. Ich persönlich kann mir keine einzige Situation vorstellen, in der ein Singleton die richtige Lösung ist. Das bedeutet nicht, dass eine solche Situation nicht existiert, aber ... sie selten zu nennen, ist eine Untertreibung.
Jalf

8
@AdamSmith bedeutet nicht, dass Sie müssen , aber es bedeutet, dass Sie so darauf zugreifen können. Und wenn Sie nicht beabsichtigen, so darauf zuzugreifen, gibt es kaum einen Grund, es überhaupt zu einem Singleton zu machen. Also Ihr Argument effektiv „ist gibt es keinen Schaden ein Singleton zu machen , wenn wir nicht behandeln es als Singleton. Ja, toll. Mein Auto nicht verschmutzt auch wenn ich darin nicht fahren. Aber dann ist es einfacher, nur überhaupt kein Auto erwerben .;) (vollständige Offenlegung: Ich habe eigentlich kein Auto)
Jalf

35
Das Schlimmste an diesem ganzen Thema ist, dass die Leute, die Singletons hassen, selten konkrete Vorschläge machen, was sie stattdessen verwenden sollen. In den Links zu Zeitschriftenartikeln und selbstveröffentlichten Blogs in diesem SO-Artikel wird beispielsweise immer wieder erläutert, warum keine Singletons verwendet werden sollen (und das sind alles gute Gründe), aber die Ersetzungen sind äußerst gering. Viel Handwinken. Diejenigen von uns, die versuchen, neuen Programmierern beizubringen, warum sie keine Singletons verwenden sollen, haben nicht viele gute Gegenbeispiele von Drittanbietern, auf die sie verweisen können, sondern nur erfundene Beispiele. Es ist müde.
Ti Strga

Antworten:


1294

Umschrieben von Brian Button:

  1. Sie werden im Allgemeinen als globale Instanz verwendet. Warum ist das so schlimm? Weil Sie die Abhängigkeiten Ihrer Anwendung in Ihrem Code verbergen, anstatt sie über die Schnittstellen verfügbar zu machen. Etwas global zu machen, um es nicht weiterzugeben, ist ein Codegeruch .

  2. Sie verstoßen gegen das Prinzip der Einzelverantwortung : Sie kontrollieren ihre eigene Schöpfung und ihren eigenen Lebenszyklus.

  3. Sie bewirken von Natur aus, dass Code eng gekoppelt ist . Dies macht es in vielen Fällen ziemlich schwierig, sie im Test vorzutäuschen.

  4. Sie tragen den Zustand für die Lebensdauer der Anwendung. Ein weiterer Erfolg beim Testen, da Sie in einer Situation enden können, in der Tests bestellt werden müssen, was ein großes Nein Nein für Komponententests ist. Warum? Weil jeder Unit-Test unabhängig vom anderen sein sollte.


324
Ich stimme dir nicht zu. Da Kommentare nur 600 Zeichen erlauben, habe ich einen Blog-Beitrag geschrieben, um dies zu kommentieren. Bitte beachten Sie den Link unten. jorudolph.wordpress.com/2009/11/22/singleton-considerations
Johannes Rudolph

61
In Punkt 1 und 4 halte ich Singletons für nützlich und in der Tat fast perfekt für das Zwischenspeichern von Daten (insbesondere aus einer Datenbank). Die Leistungssteigerung liegt weit außerhalb der Komplexität der Modellierung von Unit-Tests.
Dai Bok

56
@Dai Bok: "Zwischenspeichern von Daten (insbesondere aus einer Datenbank)" Verwenden Sie dazu das Proxy-Muster ...
paxos1977

55
Wow, tolle Antworten. Ich habe hier wahrscheinlich zu aggressive Formulierungen verwendet. Denken Sie also bitte daran, dass ich eine negativ ausgerichtete Frage beantwortet habe. Meine Antwort sollte eine kurze Liste der "Anti-Muster" sein, die durch schlechte Singleton-Nutzung entstehen. Vollständige Offenlegung; Ich benutze auch von Zeit zu Zeit Singletons. Es gibt neutralere Fragen zu SO, die sich hervorragend als Forum eignen, wenn Singletons als gute Idee angesehen werden können. Zum Beispiel stackoverflow.com/questions/228164/…
Jim Burger

21
Sie eignen sich nicht so gut zum Zwischenspeichern in einer Multithread-Umgebung. Sie können einen Cache leicht besiegen, indem mehrere Threads um eine begrenzte Ressource kämpfen. [von einem Singleton gepflegt]
Mönch

449

Singletons lösen ein (und nur ein) Problem.

Ressourcenkonflikt.

Wenn Sie eine Ressource haben, die

( 1 ) kann nur eine einzige Instanz haben, und

( 2 ) Sie müssen diese einzelne Instanz verwalten.

Du brauchst einen Singleton .

Es gibt nicht viele Beispiele. Eine Protokolldatei ist die große. Sie möchten nicht nur eine einzelne Protokolldatei verlassen. Sie möchten es richtig spülen, synchronisieren und schließen. Dies ist ein Beispiel für eine einzelne gemeinsam genutzte Ressource, die verwaltet werden muss.

Es ist selten, dass Sie einen Singleton benötigen. Der Grund, warum sie schlecht sind, ist, dass sie sich wie ein Global fühlen und ein voll bezahltes Mitglied des GoF Design Patterns- Buches sind.

Wenn Sie denken, Sie brauchen eine globale, machen Sie wahrscheinlich einen schrecklichen Designfehler.


43
Hardware ist auch ein Beispiel, oder? Eingebettete Systeme haben viel Hardware, die Singletons verwenden könnte - oder vielleicht eine große? <grin>
Jeff

170
Stimme voll und ganz zu. Es gibt viele entschlossene "Diese Praxis ist schlecht" -Gespräche, ohne zu erkennen, dass die Praxis ihren Platz haben könnte. Oft ist die Praxis nur "schlecht", weil sie häufig missbraucht wird. An dem Singleton-Muster ist an sich nichts auszusetzen, solange es ordnungsgemäß angewendet wird.
Damovisa

53
Echte Singletons nach Prinzip sind sehr selten (und eine Druckerwarteschlange ist sicherlich keine und auch keine Protokolldatei - siehe log4j). Meistens ist Hardware eher zufällig als prinzipiell ein Singleton. Alle an einen PC angeschlossene Hardware ist bestenfalls ein zufälliger Singleton (denken Sie an mehrere Monitore, Mäuse, Drucker, Soundkarten). Sogar ein 500-Millionen-Dollar-Partikeldetektor ist aus Zufalls- und Budgetgründen ein Singleton - was in Software nicht gilt, also: kein Singleton. In der Telekommunikation sind weder die Telefonnummer noch das physische Telefon Singletons (denken Sie an ISDN, Call Center).
Digitalarbeiter

23
Hardware verfügt möglicherweise nur über eine Instanz einer Vielzahl von Ressourcen, Hardware ist jedoch keine Software. Es gibt keinen Grund, eine einzelne serielle Schnittstelle auf einer Entwicklungsplatine als Singleton zu modellieren. Wenn Sie es so modellieren, wird es nur schwieriger, auf die neue Karte mit zwei Ports zu portieren!
Dash-Tom-Bang

19
Sie würden also zwei identische Klassen schreiben und viel Code duplizieren, nur damit Sie zwei Singletons haben können, die zwei Ports darstellen? Wie wäre es nur mit zwei globalen SerialPort-Objekten? Wie wäre es, die Hardware überhaupt nicht als Klassen zu modellieren? Und warum sollten Sie Singletons verwenden, die auch einen globalen Zugriff implizieren? Möchten Sie, dass jede Funktion in Ihrem Code auf die serielle Schnittstelle zugreifen kann?
Jalf

352

Einige Coding-Snobs sehen sie nur als verherrlichte Welt an. So wie viele Menschen die goto- Aussage hassen , gibt es auch andere, die die Idee hassen, jemals eine globale zu verwenden . Ich habe gesehen, dass mehrere Entwickler außerordentliche Anstrengungen unternommen haben, um ein globales Problem zu vermeiden, weil sie erwogen haben, eines als Eingeständnis für einen Fehler zu verwenden. Komisch aber wahr.

In der Praxis ist das Singleton- Muster nur eine Programmiertechnik, die ein nützlicher Bestandteil Ihres Toolkits an Konzepten ist. Von Zeit zu Zeit finden Sie vielleicht, dass es die ideale Lösung ist, und verwenden Sie es daher. Aber es nur zu verwenden, damit Sie sich rühmen können, ein Designmuster zu verwenden, ist genauso dumm, wie sich zu weigern, es jemals zu verwenden, weil es nur ein globales Muster ist .


17
Der Fehler, den ich in Singleton sehe, ist, dass die Leute sie anstelle von Globals verwenden, weil sie irgendwie "besser" sind. Das Problem ist (wie ich es sehe), dass die Dinge, die Singleton in diesen Fällen auf den Tisch bringt, irrelevant sind. (Konstrukt bei erstmaliger Verwendung ist beispielsweise in einem Nicht-Singleton trivial zu implementieren, selbst wenn der Konstruktor nicht verwendet wird.)
dash-tom-bang

21
Der Grund, warum "wir" auf sie herabblicken, ist, dass "wir" sie sehr häufig falsch und viel zu oft sehen. "Wir" wissen, dass sie ihren Grund haben.
Bjarke Freund-Hansen

5
@Phil, Sie sagten, "von Zeit zu Zeit könnten Sie feststellen, dass es die ideale Lösung ist und verwenden Sie es daher". Ok, genau in welchem Fall würden wir einen Singleton nützlich finden?
Pacerier

22
@Pacerier, wenn alle folgenden Bedingungen erfüllt sind: (1) Sie benötigen nur eine von etwas, (2) Sie müssen diese eine Sache als Argument in einer großen Anzahl von Methodenaufrufen übergeben, (3) Sie sind bereit, a zu akzeptieren Chance, eines Tages umgestalten zu müssen, um die Größe und Komplexität Ihres Codes sofort zu reduzieren, indem Sie das verdammte Ding nicht überall herumreichen.
Antinom

18
Ich habe nicht das Gefühl, dass dies irgendetwas beantwortet, es sagt nur, dass es manchmal passt, manchmal nicht. OK, aber warum und wann? Was macht diese Antwort mehr als ein Argument für Mäßigung ?
Guildenstern

219

Misko Hevery von Google hat einige interessante Artikel zu genau diesem Thema ...

Singletons sind pathologische Lügner Ein Beispiel für Unit-Tests zeigt, wie Singletons es schwierig machen können, Abhängigkeitsketten herauszufinden und eine Anwendung zu starten oder zu testen. Es ist ein ziemlich extremes Beispiel für Missbrauch, aber der Punkt, den er macht, ist immer noch gültig:

Singletons sind nichts anderes als ein globaler Staat. Durch den globalen Status können Ihre Objekte heimlich Dinge erfassen, die nicht in ihren APIs deklariert sind, und Singletons machen Ihre APIs zu pathologischen Lügnern.

Wo alle Singletons verschwunden sind, wird darauf hingewiesen, dass die Abhängigkeitsinjektion es einfach gemacht hat, Instanzen zu Konstruktoren zu bringen, die sie benötigen, was den zugrunde liegenden Bedarf hinter den schlechten, globalen Singletons, die im ersten Artikel beschrieben wurden, verringert.


8
Miskos Artikel zu diesem Thema sind das Beste, was es derzeit gibt.
Chris Mayer

22
Der erste Link behandelt eigentlich kein Problem mit Singletons, sondern geht von einer statischen Abhängigkeit innerhalb der Klasse aus. Es ist möglich, das angegebene Beispiel durch Übergabe von Parametern zu korrigieren und dennoch Singletons zu verwenden.
DGM

17
@DGM: Genau - tatsächlich gibt es eine große logische Trennung zwischen dem Teil "Begründung" des Artikels und dem Teil "Singletons sind die Ursache".
Harper Shelby

1
Der CreditCard-Artikel von Misko ist ein extremes Beispiel für den Missbrauch dieses Musters.
Alex

2
Lesen des zweiten Artikels Singletons sind tatsächlich Teil des beschriebenen Objektmodells. Anstatt global verfügbar zu sein, sind diese für die Factory-Objekte privat.
FruitBreak

121

Ich denke, die Verwirrung wird durch die Tatsache verursacht, dass die Leute die wirkliche Anwendung des Singleton-Musters nicht kennen. Ich kann das nicht genug betonen. Singleton ist kein Muster zum Umwickeln von Globalen. Das Singleton-Muster sollte nur verwendet werden, um sicherzustellen, dass zur Laufzeit nur eine Instanz einer bestimmten Klasse vorhanden ist.

Die Leute denken, Singleton sei böse, weil sie es für Globals verwenden. Aufgrund dieser Verwirrung wird auf Singleton herabgesehen. Bitte verwechseln Sie Singletons und Globals nicht. Wenn Sie es für den Zweck verwenden, für den es bestimmt war, profitieren Sie extrem vom Singleton-Muster.


20
Was ist diese eine Instanz, die in der gesamten App verfügbar ist? Oh. Global . "Singleton ist kein Muster zum Umschließen von Globals" ist entweder naiv oder irreführend, da das Muster per Definition eine Klasse um eine globale Instanz umschließt .
CHao

26
Natürlich können Sie beim Start Ihrer Anwendung eine Instanz der Klasse erstellen und diese Instanz über eine Schnittstelle in alles einfügen, was sie verwendet. Die Implementierung sollte sich nicht darum kümmern, dass es nur eine geben kann.
Scott Whitlock

4
@ Dainius: Am Ende gibt es wirklich keine. Sicher, Sie können die Instanz nicht willkürlich durch eine andere ersetzen. (Abgesehen von den Gesichtspalmen-induzierenden Momenten, in denen Sie dies tun . Ich habe tatsächlich schon setInstanceMethoden gesehen .) Das spielt jedoch kaum eine Rolle - das Weenie, das einen Singleton "brauchte", wusste auch nichts über Kapselung oder was falsch ist veränderlicher globaler Staat, also stellte er hilfreich (?) Setter für jeden zur Verfügung. Single. Feld. (Und ja, das passiert.
Sehr

4
@Dainius: Zu einem großen Teil haben wir bereits. "Komposition der Vererbung vorziehen" ist schon seit einiger Zeit eine Sache. Wenn Vererbung ist nachweislich die beste Lösung, aber sind Sie natürlich frei , es zu benutzen. Das Gleiche gilt für Singletons, Globals, Threading gotousw. Sie funktionieren möglicherweise in vielen Fällen, aber ehrlich gesagt reicht "funktioniert" nicht aus - wenn Sie gegen die konventionelle Weisheit verstoßen möchten, sollten Sie besser in der Lage sein, zu demonstrieren, wie Sie vorgehen ist besser als die herkömmlichen Lösungen. Und ich habe noch keinen solchen Fall für das Singleton-Muster gesehen.
CHao

3
Damit wir nicht aneinander vorbeigehen, spreche ich nicht nur von einer global verfügbaren Instanz. Dafür gibt es viele Fälle. Worüber ich spreche (insbesondere wenn ich "Capital-S Singleton" sage), ist das Singleton-Muster des GoF, das diese einzelne globale Instanz in die Klasse selbst einbettet, sie über eine getInstanceoder eine ähnlich benannte Methode verfügbar macht und die Existenz von a verhindert zweite Instanz. Ehrlich gesagt, könnten Sie zu diesem Zeitpunkt genauso gut nicht einmal die eine Instanz haben.
CHao

72

Eine ziemlich schlechte Sache bei Singletons ist, dass man sie nicht so einfach erweitern kann. Sie müssen im Grunde eine Art Dekorationsmuster einbauen oder ähnliches einbauen, wenn Sie ihr Verhalten ändern möchten. Wenn Sie eines Tages mehrere Möglichkeiten haben möchten, dies zu tun, kann es ziemlich schmerzhaft sein, Änderungen vorzunehmen, je nachdem, wie Sie Ihren Code gestalten.

Eine Sache, die Sie beachten sollten: Wenn Sie Singletons verwenden, versuchen Sie, diese an diejenigen weiterzugeben, die sie benötigen, anstatt sie direkt darauf zugreifen zu lassen. Andernfalls können Sie das tun, was Singleton tut Es ist ziemlich schwierig zu ändern, da jede Klasse eine Abhängigkeit einbettet, wenn sie direkt auf den Singleton zugreift.

Also im Grunde genommen:

public MyConstructor(Singleton singleton) {
    this.singleton = singleton;
}

eher, als:

public MyConstructor() {
    this.singleton = Singleton.getInstance();
}

Ich glaube, diese Art von Muster wird als Abhängigkeitsinjektion bezeichnet und allgemein als eine gute Sache angesehen.

Wie jedes Muster auch ... Denken Sie darüber nach und überlegen Sie, ob seine Verwendung in der gegebenen Situation unangemessen ist oder nicht ... Regeln gelten normalerweise als gebrochen, und Muster sollten nicht ohne nachzudenken angewendet werden.


17
Heh, wenn du das überall machst, dann müsstest du auch überall einen Verweis auf das Singelton herumgeben, und somit hast du keinen Singleton mehr. :) (Was normalerweise eine gute Sache ist, IMO.)
Bjarke Freund-Hansen

2
@ BjarkeFreund-Hansen - Unsinn, wovon redest du? Ein Singleton ist einfach eine Instanz einer Klasse, die einmal instanziiert wird. Wenn Sie auf einen solchen Singleton verweisen, wird das eigentliche Objekt nicht kopiert, sondern nur referenziert. Sie haben immer noch dasselbe Objekt (lesen Sie: Singleton).
M. Mimpen

2
@ M.Mimpen: Nein, ein Capital-S-Singleton (worüber hier gesprochen wird) ist eine Instanz einer Klasse, die (a) garantiert, dass immer nur eine Instanz existiert, und (b) über die eigene Klasse zugegriffen wird integrierter globaler Zugangspunkt. Wenn Sie erklärt haben, dass nichts anrufen soll getInstance(), ist (b) nicht mehr ganz richtig.
CHao

2
@cHao Ich folge dir nicht oder du verstehst nicht, wen ich kommentiere - was Bjarke Freund-Hansen ist. Bjarke gibt an, dass mehrere Referenzen eines Singletons zu mehreren Singletons führen. Das ist sicherlich nicht wahr, da es keine tiefen Kopien gibt.
M. Mimpen

6
@ M.Mimpen: Ich nehme seinen Kommentar, um mehr auf den semantischen Effekt zu verweisen. Sobald Sie Anrufe an geächtet haben getInstance(), haben Sie den einen nützlichen Unterschied zwischen dem Singleton-Muster und einer gewöhnlichen Referenz effektiv beseitigt. Für den Rest des Codes ist Singleness keine Eigenschaft mehr. Nur der Anrufer von muss getInstance()jemals wissen oder sich sogar darum kümmern, wie viele Instanzen es gibt. Mit nur einem Anrufer kostet es die Klasse mehr Aufwand und Flexibilität, die Singleness zuverlässig durchzusetzen, als wenn der Anrufer einfach eine Referenz speichert und wiederverwendet.
CHao

69

Das Singleton-Muster ist an sich kein Problem. Das Problem ist, dass das Muster häufig von Personen verwendet wird, die Software mit objektorientierten Tools entwickeln, ohne die OO-Konzepte genau zu verstehen. Wenn Singletons in diesem Zusammenhang eingeführt werden, neigen sie dazu, zu nicht verwaltbaren Klassen zu werden, die Hilfsmethoden für jede kleine Verwendung enthalten.

Singletons sind auch aus Testsicht ein Problem. Sie neigen dazu, isolierte Unit-Tests schwer zu schreiben. Inversion of Control (IoC) und Dependency Injection sind Muster, mit denen dieses Problem objektorientiert überwunden werden soll und die sich für Unit-Tests eignen.

In einer Müllsammelumgebung können Singletons schnell zu einem Problem bei der Speicherverwaltung werden.

Es gibt auch das Multithread-Szenario, in dem Singletons sowohl zu einem Engpass als auch zu einem Synchronisationsproblem werden können.


4
Ich weiß, es ist ein vielen Jahre alter Thread. hi @Kimoz u sagte: - Singletons können schnell zu einem Problem in Bezug auf die Speicherverwaltung werden. Ich möchte näher erläutern, welche Art von Problem in Bezug auf die Singleton- und Garbage Collection auftreten kann.
Thomas

@ Kimoz, Die Frage lautet: "Warum ist das Singleton-Muster an sich kein Problem?" und Sie haben den Punkt lediglich wiederholt, aber nicht einmal einen einzigen gültigen Anwendungsfall des Singleton-Musters angegeben.
Pacerier

@Thomas, weil ein Singleton per Definition nur in einer Instanz existiert. Daher ist es oft komplex, Null den einzigen Verweis zuzuweisen. Dies ist möglich, bedeutet jedoch, dass Sie den Punkt, nach dem der Singleton nicht mehr in Ihrer App verwendet wird, vollständig steuern. Und dies ist selten und normalerweise das Gegenteil von dem, was Singleton-Enthusiasten suchen: eine einfache Möglichkeit, eine einzelne Instanz immer zugänglich zu machen . Bei einigen DI-Framworks wie Guice oder Dagger ist es nicht möglich, einen Singleton loszuwerden, und er bleibt für immer in Erinnerung. (Obwohl in Containern bereitgestellte Singletons weitaus besser sind als hausgemachte).
Snicolas

54

Ein Singleton wird mit einer statischen Methode implementiert. Statische Methoden werden von Personen vermieden, die Unit-Tests durchführen, da sie nicht verspottet oder gestoppt werden können. Die meisten Leute auf dieser Seite sind große Befürworter von Unit-Tests. Die allgemein am meisten akzeptierte Konvention, um sie zu vermeiden, ist die Inversion des Kontrollmusters .


9
Das klingt eher nach einem Problem beim Komponententest, bei dem Objekte (Einheiten), Funktionen (Einheiten) und ganze Bibliotheken (Einheiten) getestet werden können, die jedoch mit statischen Elementen in der Klasse (auch Einheiten) fehlschlagen.
v010dya

2
Solltest du nicht trotzdem alle externen Referenzen zuordnen? Wenn ja, was ist dann ein Problem für Moc Singleton? Wenn nicht, führen Sie wirklich Unit-Tests durch?
Dainius

@Dainius: Das Verspotten von Instanzen ist viel weniger problematisch als das Verspotten von Klassen. Es ist denkbar, dass Sie die zu testende Klasse aus dem Rest der Anwendung extrahieren und mit einer gefälschten SingletonKlasse testen . Dies erschwert jedoch den Testprozess immens. Zum einen müssen Sie jetzt in der Lage sein, Klassen nach Belieben zu entladen (in den meisten Sprachen nicht wirklich eine Option) oder für jeden Test eine neue VM zu starten (lesen Sie: Tests können tausende Male so lange dauern). Für zwei ist die Abhängigkeit von Singletonjedoch ein Implementierungsdetail, das jetzt in allen Ihren Tests verloren geht.
CHao

Powermock kann statisches Zeug verspotten.
Snicolas

Bedeutet das Verspotten eines Objekts das Erstellen eines realen Objekts? Wenn durch Verspotten kein reales Objekt erstellt wird, warum ist es dann wichtig, ob die Klasse Singleton oder die Methode statisch ist?
Arun Raaj

45

Singletons sind auch schlecht, wenn es um Clustering geht . Denn dann haben Sie nicht mehr "genau einen Singleton" in Ihrer Anwendung.

Stellen Sie sich folgende Situation vor: Als Entwickler müssen Sie eine Webanwendung erstellen, die auf eine Datenbank zugreift. Um sicherzustellen, dass gleichzeitige Datenbankaufrufe nicht in Konflikt miteinander stehen, erstellen Sie einen Thread-Save SingletonDao:

public class SingletonDao {
    // songleton's static variable and getInstance() method etc. omitted
    public void writeXYZ(...){
        synchronized(...){
            // some database writing operations...
        }
    }
}

Sie sind also sicher, dass nur ein Singleton in Ihrer Anwendung vorhanden ist und alle Datenbanken diesen einzigen durchlaufen SingletonDao. Ihre Produktionsumgebung sieht jetzt folgendermaßen aus: Single Singleton

Bisher ist alles in Ordnung.

Stellen Sie sich nun vor, Sie möchten mehrere Instanzen Ihrer Webanwendung in einem Cluster einrichten. Jetzt haben Sie plötzlich so etwas:

Viele Singletons

Das klingt komisch, aber jetzt haben Sie viele Singletons in Ihrer Anwendung . Und genau das soll ein Singleton nicht sein: Viele Objekte davon haben. Dies ist besonders schlimm, wenn Sie, wie in diesem Beispiel gezeigt, synchronisierte Aufrufe an eine Datenbank tätigen möchten.

Dies ist natürlich ein Beispiel für eine schlechte Verwendung eines Singletons. Die Meldung in diesem Beispiel lautet jedoch: Sie können sich nicht darauf verlassen, dass Ihre Anwendung genau eine Instanz eines Singletons enthält - insbesondere beim Clustering.


4
Wenn Sie nicht wissen, wie man Singleton implementiert, sollten Sie das nicht tun. Wenn Sie nicht wissen, was Sie tun, sollten Sie das zuerst finden und erst danach tun, was Sie brauchen.
Dainius

3
Das ist interessant, ich habe eine Frage. Was genau ist das Problem, wenn Singletons - jeweils auf einem anderen Computer / einer anderen JVM - eine Verbindung zu einer einzelnen Datenbank herstellen? Der Singletons-Bereich gilt nur für diese bestimmte JVM, und dies gilt auch in einem Cluster. Anstatt nur philosophisch zu sagen, dass diese besondere Situation schlecht ist, weil unsere Absicht ein einziges anwendungsübergreifendes Objekt war, würde ich mich über technische Probleme freuen, die aufgrund dieser Anordnung auftreten können.
Saurabh Patil

39
  1. Es kann leicht (ab) als globale Variable verwendet werden.
  2. Klassen, die von Singletons abhängen, sind relativ schwer isoliert zu testen.

33

Monopol ist der Teufel und Singletons mit nicht schreibgeschütztem / veränderlichem Zustand sind das "wahre" Problem ...

Nachdem ich gelesen hatte, dass Singletons pathologische Lügner sind, wie in Jasons Antwort vorgeschlagen, stieß ich auf diesen kleinen Leckerbissen, der das am besten präsentierte Beispiel dafür liefert, wie Singletons oft missbraucht werden.

Global ist schlecht, weil:

  • ein. Dies führt zu einem Namespace-Konflikt
  • b. Es entlarvt den Staat auf ungerechtfertigte Weise

Wenn es um Singletons geht

  • ein. Die explizite OO-Methode zum Aufrufen verhindert die Konflikte. Zeigen Sie also auf a. ist kein Problem
  • b. Singletons ohne Staat sind (wie Fabriken) kein Problem. Singletons mit Status können wiederum in zwei Kategorien fallen, die unveränderlich sind oder einmal schreiben und viele lesen (Konfigurations- / Eigenschaftendateien). Diese sind nicht schlecht. Mutable Singletons, die eine Art Referenzinhaber sind, sind diejenigen, von denen Sie sprechen.

In der letzten Aussage bezieht er sich auf das Konzept des Blogs "Singletons sind Lügner".

Wie trifft dies auf Monopoly zu?

Um ein Monopolspiel zu beginnen, zuerst:

  • Wir legen zuerst die Regeln fest, damit alle auf der gleichen Seite sind
  • Jeder erhält zu Beginn des Spiels den gleichen Start
  • Es wird nur ein Regelwerk angezeigt, um Verwirrung zu vermeiden
  • Die Regeln dürfen sich im Laufe des Spiels nicht ändern

Für alle, die nicht wirklich Monopol gespielt haben, sind diese Standards bestenfalls ideal. Eine Niederlage im Monopol ist schwer zu schlucken, denn beim Monopol geht es um Geld. Wenn Sie verlieren, müssen Sie akribisch zusehen, wie der Rest der Spieler das Spiel beendet, und die Verluste sind normalerweise schnell und erdrückend. Daher werden die Regeln normalerweise irgendwann verdreht, um dem Eigeninteresse einiger Spieler auf Kosten der anderen zu dienen.

Sie spielen also das Monopol mit den Freunden Bob, Joe und Ed. Sie bauen schnell Ihr Imperium auf und verbrauchen exponentiell Marktanteile. Ihre Gegner werden schwächer und Sie beginnen (im übertragenen Sinne) Blut zu riechen. Ihr Kumpel Bob hat sein ganzes Geld in die Blockade so vieler minderwertiger Immobilien wie möglich gesteckt, aber er erhält nicht die erwartete hohe Kapitalrendite. Bob landet als Pech auf Ihrem Boardwalk und wird aus dem Spiel ausgeschlossen.

Jetzt geht das Spiel vom freundlichen Würfeln zum ernsthaften Geschäft. Bob wurde zum Beispiel des Scheiterns gemacht und Joe und Ed wollen nicht wie 'dieser Typ' enden. Als führender Spieler werden Sie plötzlich zum Feind. Joe und Ed üben unter dem Tisch Trades, Geldspritzen hinter dem Rücken, unterbewerteten Haustausch und im Allgemeinen alles, was Sie als Spieler schwächt, bis einer von ihnen an die Spitze steigt.

Anstatt dass einer von ihnen gewinnt, beginnt der Prozess von vorne. Plötzlich wird ein endliches Regelwerk zu einem beweglichen Ziel und das Spiel degeneriert zu einer Art sozialer Interaktion, die die Grundlage jeder hoch bewerteten Reality-TV-Show seit Survivor bilden würde. Warum, weil sich die Regeln ändern und es keinen Konsens darüber gibt, wie / warum / was sie darstellen sollen, und was noch wichtiger ist, dass niemand die Entscheidungen trifft. Zu diesem Zeitpunkt stellt jeder Spieler im Spiel seine eigenen Regeln auf und es kommt zu Chaos, bis zwei der Spieler zu müde sind, um die Scharade aufrechtzuerhalten und langsam aufzugeben.

Wenn also ein Regelbuch für ein Spiel genau einen Singleton darstellt, wäre das Monopol-Regelbuch ein Beispiel für Missbrauch.

Wie trifft dies auf die Programmierung zu?

Abgesehen von all den offensichtlichen Thread-Sicherheits- und Synchronisationsproblemen, die bei veränderlichen Singletons auftreten ... Wenn Sie über einen Datensatz verfügen, der von mehreren verschiedenen Quellen gleichzeitig gelesen / bearbeitet werden kann und während der Lebensdauer der Anwendungsausführung besteht, Es ist wahrscheinlich ein guter Zeitpunkt, einen Schritt zurückzutreten und zu fragen, ob ich hier die richtige Art von Datenstruktur verwende.

Persönlich habe ich gesehen, wie ein Programmierer einen Singleton missbraucht, indem er ihn als eine Art verdrehten Cross-Thread-Datenbankspeicher in einer Anwendung verwendet. Nachdem ich direkt an dem Code gearbeitet habe, kann ich bestätigen, dass er langsam war (wegen all der Thread-Sperren, die erforderlich sind, um ihn threadsicher zu machen) und ein Albtraum war, an dem gearbeitet werden musste (wegen der unvorhersehbaren / intermittierenden Natur von Synchronisationsfehlern) Unter Produktionsbedingungen kaum zu testen. Sicher, ein System hätte mithilfe von Polling / Signaling entwickelt werden können, um einige der Leistungsprobleme zu überwinden, aber das würde die Probleme beim Testen nicht lösen, und warum sollte man sich die Mühe machen, wenn eine "echte" Datenbank bereits die gleiche Funktionalität in einer viel robusteren Version erreichen kann? / skalierbare Weise.

Ein Singleton ist nur dann eine Option, wenn Sie das benötigen, was ein Singleton bietet. Eine schreibgeschützte Instanz eines Objekts. Dieselbe Regel sollte auch für die Eigenschaften / Elemente des Objekts gelten.


1
Was ist, wenn Singleton einige Daten einlöst?
Yola

@Yola Es würde die Anzahl der Schreibvorgänge verringern, wenn der Singleton nach einem vorgegebenen und / oder festen Zeitplan aktualisiert werden soll, wodurch die Thread-Konkurrenz verringert wird. Sie können jedoch keinen Code, der mit dem Singleton interagiert, genau testen, wenn Sie nicht eine Nicht-Singleton-Instanz verspotten, die dieselbe Verwendung simuliert. Die TDD-Leute würden wahrscheinlich einen Anfall haben, aber es würde funktionieren.
Evan Plaice

@EvanPlaice: Selbst mit einem Mock hätten Sie einige Probleme mit dem Code, der besagt Singleton.getInstance(). Eine Sprache, die Reflexion unterstützt, kann dies möglicherweise umgehen, indem Sie das Feld festlegen, in dem die One True Instance gespeichert ist. IMO, Tests werden jedoch etwas weniger vertrauenswürdig, sobald Sie sich mit dem privaten Zustand einer anderen Klasse herumgetrieben haben.
CHao

28

Bei Singleton geht es nicht um eine einzelne Instanz!

Im Gegensatz zu anderen Antworten möchte ich nicht darüber sprechen, was mit Singletons nicht stimmt, sondern Ihnen zeigen, wie mächtig und großartig sie sind, wenn sie richtig verwendet werden!

  • Problem : Singleton kann in einer Multithreading-Umgebung eine Herausforderung sein.
    Lösung : Verwenden Sie einen Bootstrap-Prozess mit einem Thread, um alle Abhängigkeiten Ihres Singletons zu initialisieren.
  • Problem : Es ist schwer, Singletons zu verspotten.
    Lösung : Verwenden Sie zum Verspotten die Methode Factory- Muster

Sie können MyModeleiner TestMyModelKlasse zuordnen , die es erbt. Überall, wo MyModelinjiziert wird, werden Sie TestMyModelinstread erhalten. - Problem : Singletons können Speicherlecks verursachen, da sie niemals entsorgt werden.
Lösung : Nun, entsorgen Sie sie! Implementieren Sie einen Rückruf in Ihre App, um Singletons ordnungsgemäß zu entsorgen. Entfernen Sie alle damit verknüpften Daten und entfernen Sie sie schließlich aus der Factory.

Wie ich beim Titel sagte, handelt es sich bei Singleton nicht um eine einzelne Instanz.

  • Singletons verbessern die Lesbarkeit : Sie können sich Ihre Klasse ansehen und sehen, welchen Singleton sie injiziert hat, um herauszufinden, welche Abhängigkeiten sie hat.
  • Singletons verbessert die Wartung : Sobald Sie eine Abhängigkeit aus einer Klasse entfernt haben, haben Sie gerade eine Singleton-Injektion gelöscht. Sie müssen keinen großen Link anderer Klassen bearbeiten, die Ihre Abhängigkeit verschoben haben (Dies ist ein stinkender Code für mich @Jim Burger )
  • Singletons verbessert Speicher und Leistung : Wenn in Ihrer Anwendung etwas passiert und die Bereitstellung einer langen Kette von Rückrufen erforderlich ist, verschwenden Sie Speicher und Leistung. Durch die Verwendung von Singleton reduzieren Sie den Mittelsmann und verbessern Ihre Leistung und Speichernutzung ( durch Vermeidung unnötiger lokaler Variablenzuweisungen).

2
Damit wird mein Hauptproblem mit Singletons nicht behoben, dh sie ermöglichen den Zugriff auf den globalen Status von einer der Tausenden von Klassen in Ihrem Projekt.
LegendLength

8
Das wäre der Zweck ...
Ilya Gazman

LegendLength Warum ist das falsch? In meiner Anwendung habe ich beispielsweise ein Singleton- SettingsObjekt, auf das von jedem Widget aus zugegriffen werden kann, damit jedes Widget weiß, wie angezeigte Zahlen formatiert werden. Wenn es kein global zugängliches Objekt wäre, müsste ich es im Konstruktor in jedes einzelne Widget im Konstruktor einfügen und den Verweis darauf als Mitgliedsvariable behalten. Dies ist eine schreckliche Verschwendung von Erinnerung und Zeit.
VK

23

Meine Antwort darauf, wie schlecht Singletons sind, lautet immer: "Sie sind schwer richtig zu machen". Viele der grundlegenden Komponenten von Sprachen sind Singletons (Klassen, Funktionen, Namespaces und sogar Operatoren), ebenso wie Komponenten in anderen Aspekten des Rechnens (localhost, Standardroute, virtuelles Dateisystem usw.), und dies ist kein Zufall. Während sie von Zeit zu Zeit Ärger und Frustration verursachen, können sie auch dazu führen, dass viele Dinge viel besser funktionieren.

Die zwei größten Probleme, die ich sehe, sind: Es wie ein globales Problem behandeln und den Singleton-Abschluss nicht definieren.

Alle reden von Singleton als Globalen, weil sie es im Grunde sind. Ein Großteil (leider nicht alle) der Schlechtigkeit in einem globalen Umfeld beruht jedoch nicht darauf, global zu sein, sondern darauf, wie Sie es verwenden. Gleiches gilt für Singletons. Umso mehr muss "Einzelinstanz" nicht unbedingt "global zugänglich" bedeuten. Es ist eher ein natürliches Nebenprodukt, und angesichts all des Schlechten, von dem wir wissen, dass es daraus resultiert, sollten wir es nicht so eilig haben, die globale Zugänglichkeit auszunutzen. Sobald Programmierer einen Singleton sehen, scheinen sie immer direkt über seine Instanzmethode darauf zuzugreifen. Stattdessen sollten Sie wie bei jedem anderen Objekt dorthin navigieren. Der meiste Code sollte nicht einmal wissen, dass es sich um einen Singleton handelt (lose Kopplung, oder?). Wenn nur ein kleines Stück Code auf das Objekt zugreift, als wäre es ein globales Objekt, wird viel Schaden rückgängig gemacht.

Der Singleton-Kontext ist auch sehr wichtig. Das bestimmende Merkmal eines Singleton ist, dass es "nur einen" gibt, aber die Wahrheit ist, dass es "nur einen" innerhalb einer Art Kontext / Namespace gibt. Sie sind normalerweise eine von: eine pro Thread, Prozess, IP-Adresse oder Cluster, können aber auch eine pro Prozessor, Maschine, Sprachennamensraum / Klassenlader / was auch immer, Subnetz, Internet usw. sein.

Der andere, weniger häufige Fehler besteht darin, den Singleton-Lebensstil zu ignorieren. Nur weil es nur einen gibt, heißt das nicht, dass ein Singleton allmächtig ist, "immer war und immer sein wird", und es ist auch im Allgemeinen nicht wünschenswert (Objekte ohne Anfang und Ende verletzen alle Arten nützlicher Annahmen im Code und sollten nur verwendet werden unter den verzweifeltsten Umständen.

Wenn Sie diese Fehler vermeiden, können Singletons immer noch eine PITA sein, aber es ist bereit zu sehen, dass viele der schlimmsten Probleme erheblich gemildert werden. Stellen Sie sich einen Java-Singleton vor, der explizit als einmal pro Klassenladeprogramm definiert ist (was bedeutet, dass eine Thread-Sicherheitsrichtlinie erforderlich ist), mit definierten Erstellungs- und Zerstörungsmethoden und einem Lebenszyklus, der vorschreibt, wann und wie sie aufgerufen werden und dessen "Instanz" -Methode vorhanden ist Paketschutz, sodass der Zugriff im Allgemeinen über andere, nicht globale Objekte erfolgt. Immer noch eine potenzielle Problemquelle, aber sicherlich viel weniger Probleme.

Leider, anstatt gute Beispiele für Singletons zu vermitteln. Wir unterrichten schlechte Beispiele, lassen Programmierer eine Weile davonlaufen und sagen ihnen dann, dass es sich um ein schlechtes Entwurfsmuster handelt.


23

Siehe Wikipedia Singleton_pattern

Es wird auch von einigen Leuten als Anti-Muster angesehen, die der Meinung sind, dass es übermäßig verwendet wird, was zu unnötigen Einschränkungen in Situationen führt, in denen eine einzige Instanz einer Klasse nicht tatsächlich erforderlich ist. [1] [2] [3] [4]

Referenzen (nur relevante Referenzen aus dem Artikel)

  1. ^ Alex Miller. Muster, die ich hasse # 1: Singleton , Juli 2007
  2. ^ Scott Densmore. Warum Singletons böse sind , Mai 2004
  3. ^ Steve Yegge. Singletons als dumm angesehen , September 2004
  4. ^ JB Rainsberger, IBM. Verwenden Sie Ihre Singletons mit Bedacht , Juli 2001

8
Eine Beschreibung des Musters erklärt nicht, warum es als böse angesehen wird ...
Jrgns

2
Kaum fair: "Es wird auch von einigen Leuten als Anti-Muster angesehen, die das Gefühl haben, dass es übermäßig verwendet wird, was zu unnötigen Einschränkungen in Situationen führt, in denen eine einzige Instanz einer Klasse nicht wirklich erforderlich ist." Schauen Sie sich die Referenzen an ... Jedenfalls gibt es RTFM für mich.
GUI Junkie

17

Es ist nicht so, dass Singletons selbst schlecht sind, aber das GoF-Designmuster ist es. Das einzige wirklich gültige Argument ist, dass sich das GoF-Entwurfsmuster nicht für Tests eignet, insbesondere wenn Tests parallel ausgeführt werden.

Die Verwendung einer einzelnen Instanz einer Klasse ist ein gültiges Konstrukt, solange Sie die folgenden Mittel im Code anwenden:

  1. Stellen Sie sicher, dass die Klasse, die als Singleton verwendet wird, eine Schnittstelle implementiert. Dadurch können Stubs oder Mocks über dieselbe Schnittstelle implementiert werden

  2. Stellen Sie sicher, dass der Singleton threadsicher ist. Das ist eine gegebene.

  3. Der Singleton sollte von Natur aus einfach und nicht übermäßig kompliziert sein.

  4. Verwenden Sie zur Laufzeit Ihrer Anwendung, in der Singletons an ein bestimmtes Objekt übergeben werden müssen, eine Klassenfactory, die dieses Objekt erstellt, und lassen Sie die Klassenfactory die Singleton-Instanz an die Klasse übergeben, die sie benötigt.

  5. Erstellen Sie während des Testens und um deterministisches Verhalten sicherzustellen, die Singleton-Klasse als separate Instanz, entweder als tatsächliche Klasse selbst oder als Stub / Mock, der ihr Verhalten implementiert, und übergeben Sie es unverändert an die Klasse, die es benötigt. Verwenden Sie nicht den Klassenfaktor, der das zu testende Objekt erstellt, das den Singleton während des Tests benötigt, da es die einzelne globale Instanz davon übergeben wird, wodurch der Zweck zunichte gemacht wird.

Wir haben Singletons in unseren Lösungen mit großem Erfolg verwendet, die testbar sind und deterministisches Verhalten in parallelen Testlaufströmen sicherstellen.


+1, Endlich eine Antwort, die angibt, wann ein Singleton gültig sein kann.
Pacerier

16

Ich möchte die 4 Punkte in der akzeptierten Antwort ansprechen, hoffentlich kann jemand erklären, warum ich falsch liege.

  1. Warum ist es schlecht, Abhängigkeiten in Ihrem Code zu verbergen? Es gibt bereits Dutzende versteckter Abhängigkeiten (C-Laufzeitaufrufe, OS-API-Aufrufe, globale Funktionsaufrufe), und Singleton-Abhängigkeiten sind leicht zu finden (Suche zum Beispiel ()).

    "Etwas global zu machen, um es nicht weiterzugeben, ist ein Code-Geruch." Warum wird nichts weitergegeben, um zu vermeiden, dass es als Singleton zu einem Code-Geruch wird?

    Wenn Sie ein Objekt durch 10 Funktionen in einem Aufrufstapel übergeben, um einen Singleton zu vermeiden, ist das so großartig?

  2. Prinzip der Einzelverantwortung: Ich denke, das ist etwas vage und hängt von Ihrer Definition der Verantwortung ab. Eine relevante Frage wäre, warum es wichtig ist, diese spezifische "Verantwortung" einer Klasse hinzuzufügen ?

  3. Warum ist die Übergabe eines Objekts an eine Klasse enger gekoppelt als die Verwendung dieses Objekts als Singleton innerhalb der Klasse?

  4. Warum ändert sich, wie lange der Staat dauert? Singletons können manuell erstellt oder zerstört werden, sodass das Steuerelement weiterhin vorhanden ist und Sie die Lebensdauer der Lebensdauer eines Nicht-Singleton-Objekts anpassen können.

In Bezug auf Unit-Tests:

  • Nicht alle Klassen müssen einheitlich getestet werden
  • Nicht alle Klassen, die einem Unit-Test unterzogen werden müssen, müssen die Implementierung des Singletons ändern
  • wenn sie tun muss sein Gerät getestet und tun , dass die Umsetzung zu ändern, ist es einfach , über Dependency Injection , eine Klasse zu ändern , um eine Singleton von der Nutzung des Singleton mit an sie übergeben.

1
1. All diese versteckten Abhängigkeiten? Die sind auch schlecht. Versteckte Abhängigkeiten sind immer böse. Aber im Fall von CRT und OS sind sie bereits da und sie sind die einzige Möglichkeit, etwas zu erledigen. (Versuchen Sie, ein C-Programm zu schreiben, ohne die Laufzeit oder das Betriebssystem zu verwenden.) Diese Fähigkeit, Dinge zu tun, überwiegt bei weitem die negativen. Singletons in unserem Code bekommen diesen Luxus nicht; Da sie fest in unserem Kontroll- und Verantwortungsbereich liegen, sollte jede Verwendung (sprich: jede zusätzliche versteckte Abhängigkeit) als einzig vernünftiger Weg zur Erledigung von Aufgaben gerechtfertigt sein. Sehr, sehr wenige von ihnen sind es tatsächlich.
CHao

2. "Verantwortung" wird normalerweise als "Grund zur Änderung" definiert. Ein Singleton verwaltet am Ende sowohl seine Lebensdauer als auch seine eigentliche Arbeit. Wenn Sie ändern, wie der Job ausgeführt wird oder wie das Objektdiagramm erstellt wird, müssen Sie die Klasse ändern. Wenn Sie jedoch einen anderen Code haben, der das Objektdiagramm erstellt, und Ihre Klasse nur ihre eigentliche Aufgabe erfüllt, kann dieser Initialisierungscode das Objektdiagramm nach Belieben einrichten. Alles ist viel modularer und testbarer, da Sie Testdoppel einfügen und alles nach Belieben abreißen können und sich nicht mehr auf versteckte bewegliche Teile verlassen müssen.
CHao

1
@cHao: 1. Es ist gut, dass Sie erkennen, dass die "Fähigkeit, Dinge zu tun, ihre Negative bei weitem überwiegt". Ein Beispiel wäre ein Protokollierungsframework. Es ist böse, die Übergabe eines globalen Protokollierungsobjekts durch Abhängigkeitsinjektion durch Ebenen von Funktionsaufrufen zu beauftragen, wenn dies bedeutet, dass Entwickler von der Protokollierung abgehalten werden. Daher Singleton. 3. Ihr enger Kopplungspunkt ist nur relevant, wenn Sie möchten, dass Clients der Klasse das Verhalten über Polymorphismus steuern. Das ist oft nicht der Fall.
Jon

2
4. Ich bin nicht sicher, ob "nicht manuell zerstört werden kann" eine logische Implikation von "kann nur eine Instanz sein" ist. Wenn Sie Singleton so definieren, dann sprechen wir nicht über dasselbe. Alle Klassen sollten nicht einheitlich getestet werden. Dies ist eine Geschäftsentscheidung, und manchmal haben Markteinführungszeiten oder Entwicklungskosten Vorrang.
Jon

1
3. Wenn Sie keinen Polymorphismus wollen, brauchen Sie nicht einmal eine Instanz. Sie können es genauso gut zu einer statischen Klasse machen, weil Sie sich sowieso an ein einzelnes Objekt und eine einzelne Implementierung binden - und zumindest dann belügt Sie die API nicht so sehr.
CHao

15

Vince Huston hat diese Kriterien, die mir vernünftig erscheinen:

Singleton sollte nur berücksichtigt werden, wenn alle drei der folgenden Kriterien erfüllt sind:

  • Das Eigentum an der einzelnen Instanz kann nicht angemessen zugewiesen werden
  • Eine verzögerte Initialisierung ist wünschenswert
  • Ein globaler Zugriff ist nicht anders vorgesehen

Wenn der Besitz der einzelnen Instanz, wann und wie die Initialisierung erfolgt und der globale Zugriff keine Probleme darstellt, ist Singleton nicht ausreichend interessant.


14

Singletons sind aus puristischer Sicht schlecht.

Aus praktischer Sicht ist ein Singleton ein Kompromiss zwischen Entwicklungszeit und Komplexität .

Wenn Sie wissen, dass sich Ihre Anwendung nicht so stark ändert, sind sie in Ordnung. Sie müssen nur wissen, dass Sie möglicherweise Änderungen vornehmen müssen, wenn sich Ihre Anforderungen auf unerwartete Weise ändern (was in den meisten Fällen ziemlich in Ordnung ist).

Singletons erschweren manchmal auch das Testen von Einheiten .


3
Ich habe die Erfahrung gemacht, dass das Beginnen mit Singletons Sie auch auf kurze Sicht wirklich verletzen wird, auf lange Sicht nicht. Dieser Effekt könnte mich weniger treffen, wenn die Anwendung bereits seit einiger Zeit vorhanden ist und wahrscheinlich bereits von anderen Singletons befallen ist. Von Anfang an anfangen? Vermeiden Sie sie um jeden Preis! Möchten Sie eine einzelne Instanz eines Objekts? Lassen Sie eine Fabrik die Instanziierung dieses Objekts verwalten.
Sven

1
AFAIK Singleton ist eine Fabrikmethode. Daher verstehe ich Ihre Empfehlung nicht.
Tacone

3
"A factory"! = "Eine factory_method, die nicht von den anderen nützlichen Dingen in derselben Klasse getrennt werden kann"
Sven

13

An dem Muster ist an sich nichts auszusetzen, vorausgesetzt, es wird für einen Aspekt Ihres Modells verwendet, der wirklich einzeln ist.

Ich glaube, das Spiel ist auf seine Überbeanspruchung zurückzuführen, was wiederum auf die Tatsache zurückzuführen ist, dass es das am einfachsten zu verstehende und zu implementierende Muster ist.


6
Ich denke, die Gegenreaktion hat mehr damit zu tun, dass Leute, die formalisierte Unit-Tests praktizieren, erkennen, dass sie ein Albtraum sind, mit dem sie sich befassen müssen.
Jim Burger

1
Ich bin mir nicht sicher, warum dies bei -1 liegt. Es ist nicht die aussagekräftigste Antwort, aber er liegt auch nicht falsch.
Outlaw Programmer

13

Ich werde das Gut / Böse-Argument nicht kommentieren, aber ich habe sie seit dem Frühling nicht mehr benutzt . Die Verwendung der Abhängigkeitsinjektion hat meine Anforderungen an Singleton, Servicelokalisierer und Fabriken so gut wie beseitigt. Ich finde dies eine viel produktivere und sauberere Umgebung, zumindest für die Art der Arbeit, die ich mache (Java-basierte Webanwendungen).


3
Sind Spring Beans nicht standardmäßig Singletons?
schlank

3
Ja, aber ich meine "Codierung" von Singletons. Ich habe keinen Singleton geschrieben (dh mit einem privaten Konstruktor yada yada yada gemäß Entwurfsmuster) - aber ja, mit spring verwende ich eine einzelne Instanz.
prule

4
Wenn andere es tun, ist es in Ordnung (wenn ich das später benutze), aber wenn andere es tun und ich es nicht benutze, ist es böse?
Dainius

11

Singleton ist ein Muster und kann wie jedes andere Werkzeug verwendet oder missbraucht werden.

Der schlechte Teil eines Singletons ist im Allgemeinen der Benutzer (oder sollte ich die unangemessene Verwendung eines Singletons für Dinge sagen, für die er nicht entwickelt wurde). Der größte Täter verwendet einen Singleton als gefälschte globale Variable.


1
Danke Loki :) Genau mein Punkt. Die ganze Hexenjagd erinnert mich an die Goto-Debatte. Tools sind für Leute gedacht, die wissen, wie man sie benutzt. Die Verwendung eines Werkzeugs, das Sie nicht mögen, kann für Sie gefährlich sein. Vermeiden Sie es daher, aber sagen Sie anderen NICHT, dass sie es nicht verwenden / nicht lernen sollen, es richtig zu verwenden. Ich bin nicht gerade "Pro-Singleton", aber ich mag die Tatsache, dass ich ein solches Tool habe und ich kann fühlen, wo es angemessen ist, es zu verwenden. Zeitraum.
dkellner

8

Wenn Sie Code mit Singletons schreiben, z. B. einem Logger oder einer Datenbankverbindung, und anschließend feststellen, dass Sie mehr als ein Protokoll oder mehr als eine Datenbank benötigen, sind Sie in Schwierigkeiten.

Singletons machen es sehr schwer, sich von ihnen zu normalen Objekten zu bewegen.

Außerdem ist es zu einfach, einen nicht threadsicheren Singleton zu schreiben.

Anstatt Singletons zu verwenden, sollten Sie alle erforderlichen Dienstprogrammobjekte von Funktion zu Funktion übergeben. Das kann vereinfacht werden, wenn Sie alle wie folgt in ein Hilfsobjekt einwickeln:

void some_class::some_function(parameters, service_provider& srv)
{
    srv.get<error_logger>().log("Hi there!");
    this->another_function(some_other_parameters, srv);
}

4
Das Übergeben von Argumenten für jede Methode ist brillant. Es sollte mindestens die zehn wichtigsten Methoden zur Verschmutzung Ihrer API enthalten.
Dainius

Dies ist ein offensichtlicher Nachteil, der von der Sprache mithilfe von Argumenten hätte behandelt werden müssen, die sich irgendwie in verschachtelte Aufrufe ausbreiten, aber ohne eine solche Unterstützung muss dies manuell erfolgen. Bedenken Sie, dass selbst natürliche Singletons wie ein Objekt, das die Systemuhr darstellt, Probleme verursachen können. Wie werden Sie beispielsweise den uhrabhängigen Code (denken Sie an einen Stromzähler mit mehreren Tarifen) mit Tests abdecken?
Roman Odaisky

hängt davon ab, was Sie testen, wenn Sie Singleton nicht testen, warum es Sie interessiert, wie es sich verhält, wenn Ihre 2 Remote-Objekte voneinander abhängig sind, ist es kein Singleton-Problem.
Dainius

Können Sie angeben, welche Sprache in verschachtelte Aufrufe übertragen werden soll?
Dainius

1
Wenn Sie ein Modul haben, das von einem anderen abhängt und Sie es nicht kennen / nicht zuordnen können, werden Sie es schwer haben, unabhängig davon, ob es Singleton ist oder nicht. Leute verwenden Vererbung viel zu oft, wo sie Komposition verwenden sollten, aber Sie sagen nicht, dass Vererbung schlecht ist, und OOP sollte um jeden Preis vermieden werden, weil so viele Leute dort Designfehler machen, oder?
Dainius

8

Das Problem mit Singletons ist das Problem des größeren Umfangs und damit der Kopplung . Es ist nicht zu leugnen, dass es einige Situationen gibt, in denen Sie Zugriff auf eine einzelne Instanz benötigen, und dies kann auf andere Weise erreicht werden.

Ich ziehe es jetzt vor, einen IoC-Container ( Inversion of Control ) zu entwerfen und zuzulassen, dass die Lebensdauern vom Container gesteuert werden. Dies gibt Ihnen den Vorteil, dass die von der Instanz abhängigen Klassen nicht wissen, dass es eine einzelne Instanz gibt. Die Lebensdauer des Singletons kann in Zukunft geändert werden. Ein solches Beispiel, auf das ich kürzlich gestoßen bin, war eine einfache Anpassung von Single-Threaded zu Multi-Threaded.

FWIW, wenn es sich um eine PIA handelt, wenn Sie versuchen, sie einem Unit-Test zu unterziehen, wird sie an PIA weitergeleitet, wenn Sie versuchen, sie zu debuggen, Fehler zu beheben oder zu verbessern.



7

Singletons sind NICHT schlecht. Es ist nur schlecht, wenn Sie etwas global Einzigartiges machen, das nicht global einzigartig ist.

Es gibt jedoch "Application Scope Services" (denken Sie an ein Messaging-System, mit dem Komponenten interagieren) - diese CALLS für einen Singleton, eine "MessageQueue" -Klasse mit der Methode "SendMessage (...)".

Sie können dann von überall aus Folgendes tun:

MessageQueue.Current.SendMessage (neue MailArrivedMessage (...));

Und natürlich:

MessageQueue.Current.RegisterReceiver (this);

in Klassen, die IMessageReceiver implementieren.


6
Und wenn ich eine zweite Nachrichtenwarteschlange mit einem kleineren Bereich erstellen möchte, warum sollte ich Ihren Code dann nicht wiederverwenden dürfen, um ihn zu erstellen? Ein Singleton verhindert das. Wenn Sie jedoch nur eine reguläre Nachrichtenwarteschlangenklasse erstellt und dann eine globale Instanz als "Anwendungsbereich" erstellt haben, könnte ich eine zweite Instanz für eine andere Verwendung erstellen. Aber wenn Sie die Klasse zu einem Singleton machen, müsste ich eine zweite Klasse für Nachrichtenwarteschlangen schreiben .
Jalf

1
Warum sollten wir nicht einfach eine globale CustomerOrderList haben, damit wir sie von überall wie MessageQueue aus gut aufrufen können? Ich glaube, die Antwort ist für beide gleich: Es wird effektiv eine globale Variable erstellt.
LegendLength

7

Zu viele Leute fügen Objekte, die nicht threadsicher sind, in ein Singleton-Muster ein. Ich habe Beispiele für einen DataContext ( LINQ to SQL ) gesehen, der in einem Singleton-Muster erstellt wurde, obwohl der DataContext nicht threadsicher und lediglich ein Arbeitseinheitsobjekt ist.


Viele Leute schreiben unsicheren Multithread-Code. Bedeutet das, dass wir Threads entfernen sollten?
Dainius

@ Dainius: In der Tat ja. IMO sollte das Standard-Parallelitätsmodell mehrere Prozesse sein, mit der Möglichkeit, dass zwei Prozesse (1) Nachrichten problemlos aneinander weiterleiten und / oder (2) den Speicher nach Bedarf gemeinsam nutzen. Threading ist nützlich, wenn Sie alles teilen möchten , aber das wollen Sie nie wirklich. Es könnte durch gemeinsame Nutzung des gesamten Adressraums emuliert werden, dies würde jedoch auch als Anti-Pattern angesehen.
CHao

@Dainius: Und das setzt natürlich voraus, dass überhaupt eine ehrliche Parallelität erforderlich ist. Oft ist alles, was Sie wollen, in der Lage zu sein, eine Sache zu tun, während Sie darauf warten, dass das Betriebssystem etwas anderes tut. (Aktualisieren der Benutzeroberfläche beispielsweise beim Lesen von einem Socket.) Eine anständige asynchrone API kann in den allermeisten Fällen ein vollständiges Threading unnötig machen.
CHao

Wenn Sie also eine riesige Datenmatrix haben, die Sie verarbeiten müssen, ist es besser, dies in einem einzigen Thread zu tun, da dies für viele Menschen möglicherweise falsch ist.
Dainius

@ Dainius: In vielen Fällen ja. (Threading kann Sie tatsächlich verlangsamen , wenn Sie dem Mix Synchronisation hinzufügen. Dies ist ein hervorragendes Beispiel dafür, warum Multithreading nicht der erste Gedanke der meisten Menschen sein sollte.) In anderen Fällen ist es besser, zwei Prozesse zu haben, die sich nur teilen das benötigte Zeug. Natürlich müssten Sie dafür sorgen, dass die Prozesse den Speicher gemeinsam nutzen. Aber ehrlich gesagt sehe ich das als eine gute Sache an - zumindest weitaus besser, als standardmäßig den Modus "Alles teilen" zu verwenden. Sie müssen explizit angeben (und im Idealfall wissen), welche Teile gemeinsam genutzt werden und welche Teile threadsicher sein müssen.
CHao

6

Hier ist noch eine Sache über Singletons, die noch niemand gesagt hat.

In den meisten Fällen ist "Singletonity" eher ein Detail der Implementierung für eine Klasse als ein Merkmal ihrer Schnittstelle. Die Inversion des Steuercontainers kann dieses Merkmal vor Klassenbenutzern verbergen. Sie müssen Ihre Klasse nur als Singleton markieren ( @Singletonz. B. mit Anmerkungen in Java) und fertig. IoCC erledigt den Rest. Sie müssen keinen globalen Zugriff auf Ihre Singleton-Instanz bereitstellen, da der Zugriff bereits von IoCC verwaltet wird. Somit ist an IoC Singletons nichts auszusetzen.

GoF-Singletons im Gegensatz zu IoC-Singletons sollen "Singletonity" in der Schnittstelle durch die Methode getInstance () verfügbar machen, damit sie unter allem leiden, was oben gesagt wurde.


3
Meiner Meinung nach ist "Singletonity" ein Laufzeitumgebungsdetail und sollte vom Programmierer, der den Klassencode geschrieben hat, nicht berücksichtigt werden. Es ist vielmehr eine Überlegung der Klasse USER. Nur der USER weiß, wie viele Instanzen tatsächlich benötigt werden.
Earth Engine

5

Singletons sind nicht böse, wenn Sie sie richtig und minimal verwenden . Es gibt viele andere gute Designmuster, die irgendwann die Anforderungen von Singleton ersetzen (und auch die besten Ergebnisse liefern). Aber einige Programmierer sind sich dieser guten Muster nicht bewusst und verwenden den Singleton für alle Fälle, was den Singleton für sie böse macht.


Genial! Stimme voll und ganz zu! Aber Sie könnten und sollten vielleicht noch viel mehr ausarbeiten. B. erweitern, welche Entwurfsmuster am häufigsten ignoriert werden und wie Singletons "richtig und minimal" verwendet werden. Leichter gesagt als getan ! : P
Cregox

Grundsätzlich besteht die Alternative darin, Objekte über Methodenparameter weiterzugeben, anstatt über den globalen Status darauf zuzugreifen.
LegendLength

5

Erstens sollten eine Klasse und ihre Mitarbeiter zunächst ihren beabsichtigten Zweck erfüllen, anstatt sich auf abhängige Personen zu konzentrieren. Das Lebenszyklusmanagement (wenn Instanzen erstellt werden und wenn sie außerhalb des Gültigkeitsbereichs liegen) sollte nicht Teil der Klassenverantwortung sein. Die bewährte Vorgehensweise hierfür besteht darin, eine neue Komponente zu erstellen oder zu konfigurieren, um Abhängigkeiten mithilfe der Abhängigkeitsinjektion zu verwalten.

Oft wird Software komplizierter. Es ist sinnvoll, mehrere unabhängige Instanzen der Singleton-Klasse mit unterschiedlichem Status zu haben. In solchen Fällen ist es falsch, Code festzulegen, um einfach den Singleton zu greifen. Die Verwendung Singleton.getInstance()mag für kleine einfache Systeme in Ordnung sein, funktioniert jedoch nicht / skaliert nicht, wenn eine andere Instanz derselben Klasse benötigt wird.

Keine Klasse sollte als Singleton betrachtet werden, sondern sollte eine Anwendung ihrer Verwendung oder der Art und Weise sein, wie sie zum Konfigurieren von abhängigen Personen verwendet wird. Für eine schnelle und unangenehme Angelegenheit spielt dies keine Rolle - nur eine lauharte Codierung sagt, dass Dateipfade keine Rolle spielen, aber für größere Anwendungen müssen solche Abhängigkeiten mit DI herausgerechnet und in geeigneterer Weise verwaltet werden.

Die Probleme, die Singleton beim Testen verursacht, sind ein Symptom für den fest codierten Einzelanwendungsfall / die Umgebung. Die Testsuite und die vielen Tests sind jeweils einzeln und trennen etwas, das nicht mit der harten Codierung eines Singletons kompatibel ist.


4

Da es sich im Grunde genommen um objektorientierte globale Variablen handelt, können Sie Ihre Klassen normalerweise so gestalten, dass Sie sie nicht benötigen.


Wenn Ihre Klasse nicht auf eine einzelne Instanz beschränkt ist, benötigen Sie statische Mitglieder in Ihrer Klasse, die von Semaphoren verwaltet werden, die fast dasselbe bewirken! Was ist Ihre vorgeschlagene Alternative?
Jeach

Ich habe eine riesige Anwendung mit einem "MainScreen". Dieser Bildschirm öffnet viele kleinere modale / nicht modale Fenster / UI-Formulare. Meiner Meinung nach sollte der MainScreen ein Singleton sein, damit beispielsweise ein Widget irgendwo in einer entfernten Ecke der Anwendung seinen Status in der Statusleiste des MainScreens anzeigen möchte. Alles, was es tun muss, ist MainScreen.getInstance (). SetStatus ( "Etwas Text"); Was schlagen Sie als Alternative vor? Pass MainScreen über die gesamte Anwendung? : D
Salvin Francis

2
@SalvinFrancis: Ich würde empfehlen, dass Sie keine Objekte mehr haben, die sich um Dinge kümmern, die sie nicht interessieren sollten, und sich über die App schleichen, um miteinander herumzuspielen. :) Dein Beispiel wäre viel besser mit Ereignissen gemacht. Wenn Sie Ereignisse korrekt ausführen, muss sich ein Widget nicht einmal darum kümmern, ob überhaupt ein MainScreen vorhanden ist. Es wird nur "Hey, es ist etwas passiert" gesendet, und was auch immer das Ereignis "Zeug ist passiert" abonniert hat (sei es ein MainScreen, ein WidgetTest oder etwas ganz anderes!), entscheidet, wie es reagieren möchte. So soll OOP sowieso gemacht werden. :)
CHao

1
@Salvin Überlegen Sie, wie schwierig es ist, beim Debuggen über MainScreen nachzudenken, wenn es von vielen Komponenten "stillschweigend" aktualisiert wird. Ihr Beispiel ist ein perfekter Grund, warum Singletons schlecht sind.
LegendLength
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.