Singleton: Wie soll es verwendet werden?


291

Bearbeiten: Von einer anderen Frage habe ich eine Antwort bereitgestellt, die Links zu vielen Fragen / Antworten zu Singletons enthält: Weitere Informationen zu Singletons hier:

Also habe ich den Thread Singletons gelesen : gutes Design oder eine Krücke?
Und der Streit tobt immer noch.

Ich sehe Singletons als Designmuster (gut und schlecht).

Das Problem mit Singleton ist nicht das Muster, sondern die Benutzer (sorry an alle). Alle und ihr Vater glauben, dass sie eines richtig umsetzen können (und aus den vielen Interviews, die ich gemacht habe, können die meisten Menschen nicht). Auch weil jeder glaubt, einen korrekten Singleton implementieren zu können, missbrauchen sie das Muster und verwenden es in Situationen, die nicht angemessen sind (Ersetzen globaler Variablen durch Singletons!).

Die wichtigsten Fragen, die beantwortet werden müssen, sind:

  • Wann sollten Sie einen Singleton verwenden?
  • Wie implementiere ich einen Singleton richtig?

Ich hoffe für diesen Artikel, dass wir an einem einzigen Ort (anstatt mehrere Websites googeln und durchsuchen zu müssen) eine maßgebliche Quelle dafür sammeln können, wann (und wie) ein Singleton richtig verwendet wird. Ebenfalls angemessen wäre eine Liste von Anti-Usages und häufigen schlechten Implementierungen, die erklären, warum sie nicht funktionieren, und für gute Implementierungen ihre Schwächen.


Also bring den Ball ins Rollen:
Ich werde meine Hand hochhalten und sagen, dass ich das benutze, aber wahrscheinlich Probleme habe.
Ich mag "Scott Myers" Umgang mit dem Thema in seinen Büchern "Effective C ++"

Gute Situationen, um Singletons zu verwenden (nicht viele):

  • Protokollierungs-Frameworks
  • Fadenrecyclingpools
/*
 * C++ Singleton
 * Limitation: Single Threaded Design
 * See: http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
 *      For problems associated with locking in multi threaded applications
 *
 * Limitation:
 * If you use this Singleton (A) within a destructor of another Singleton (B)
 * This Singleton (A) must be fully constructed before the constructor of (B)
 * is called.
 */
class MySingleton
{
    private:
        // Private Constructor
        MySingleton();
        // Stop the compiler generating methods of copy the object
        MySingleton(MySingleton const& copy);            // Not Implemented
        MySingleton& operator=(MySingleton const& copy); // Not Implemented

    public:
        static MySingleton& getInstance()
        {
            // The only instance
            // Guaranteed to be lazy initialized
            // Guaranteed that it will be destroyed correctly
            static MySingleton instance;
            return instance;
        }
};

OK. Lassen Sie uns gemeinsam Kritik und andere Implementierungen zusammenbringen.
:-)


36
Was ist, wenn Sie später entscheiden, dass Sie mehrere Logger möchten? Oder mehrere Thread-Pools? Wenn Sie nur einen Logger möchten, erstellen Sie nur eine Instanz und machen Sie sie global. Singletons sind nur dann gut, wenn Sie unbedingt nur einen brauchen und es global sein muss, IMHO.

3
Wer hat gesagt, ein Framework kann nur 1 Logger-Instanz haben. Ein Singelton, der Framework repräsentiert. Framwork kann Ihnen dann bestimmte Logger geben.
Martin York

Ja. Ich würde keinen Singeltong als Threadpool verwenden. Wirf einfach Ideen aus, um Antworten zu finden.
Martin York

@ Dan Singleton, der das Strategiemuster implementiert. Verhalten wird von Singleton abstrahiert. Singleton ist ein einziger Einstiegspunkt. Haben Sie nicht zwei Logger, sondern einen Logger, der entscheiden kann, wie protokolliert werden soll. Sie können nicht nur in einem Protokoll gleichzeitig ausgeben, Sie müssen auch nicht zwei haben.
Lee Louviere

5
Xaade: Was ist, wenn Sie sich bei zwei Dateien anmelden möchten? Oder in eine Datenbank? Oder eine Netzwerkbuchse? Oder ein GUI-Widget? Punkt ist, fügen Sie keine künstlichen Einschränkungen hinzu - es besteht keine Notwendigkeit dazu. Wie oft haben Sie jemals versehentlich zwei for-Schleifen anstelle von nur einer erstellt? Wenn Sie nur einen Logger möchten, erstellen Sie nur einen.

Antworten:


182

Sie alle liegen falsch. Lies die Frage. Antworten:

Verwenden Sie einen Singleton, wenn:

  • Sie müssen nur ein Objekt eines Typs im System haben

Verwenden Sie keinen Singleton, wenn:

  • Sie möchten Speicherplatz sparen
  • Sie möchten etwas Neues ausprobieren
  • Sie möchten zeigen, wie viel Sie wissen
  • Weil alle anderen es tun (siehe Frachtkult-Programmierer in Wikipedia)
  • In Benutzeroberflächen-Widgets
  • Es soll ein Cache sein
  • In Strings
  • In Sitzungen
  • Ich kann den ganzen Tag gehen

So erstellen Sie den besten Singleton:

  • Je kleiner, desto besser. Ich bin ein Minimalist
  • Stellen Sie sicher, dass es threadsicher ist
  • Stellen Sie sicher, dass es niemals null ist
  • Stellen Sie sicher, dass es nur einmal erstellt wird
  • Lazy oder Systeminitialisierung? Bis zu Ihren Anforderungen
  • Manchmal erstellt das Betriebssystem oder die JVM Singletons für Sie (z. B. in Java ist jede Klassendefinition ein Singleton).
  • Stellen Sie einen Destruktor bereit oder finden Sie heraus, wie Sie Ressourcen entsorgen können
  • Verwenden Sie wenig Speicher

14
Eigentlich denke ich, dass Sie auch nicht ganz richtig sind. Ich würde anders formulieren als: „Wenn Sie benötigen ein und nur ein Objekt eines Typs in System haben , und Sie müssen den globalen Zugriff auf ihn haben“ Emphasis auf Notwendigkeit ist meine - tun Sie es nicht , wenn seine bequem, nur , wenn Sie MUSS es haben.

91
Du liegst auch falsch. Wenn Sie nur ein Objekt benötigen, erstellen Sie nur ein Objekt. Wenn es keine logische Möglichkeit gibt, zwei Instanzen jemals unterzubringen, ohne die Anwendung irreversibel zu beschädigen, sollten Sie in Betracht ziehen, sie zu einem Singleton zu machen. Und dann gibt es noch den anderen Aspekt, den globalen Zugriff: Wenn Sie keinen globalen Zugriff auf die Instanz benötigen, sollte es kein Singleton sein.
Jalf

4
Zur Änderung geschlossen, zur Erweiterung geöffnet. Das Problem ist, dass Sie einen Singleton nicht zu einem Duoton oder Tripleton erweitern können. Es steckt als Singleton fest.
Lee Louviere

2
@ enzom83: Ein Capital-S-Singleton enthält Code, um seine Singleness sicherzustellen. Wenn Sie nur eine Instanz möchten, können Sie diesen Code verlieren und einfach selbst eine Instanz erstellen. Dies spart Ihnen Speicherplatz für eine einzelne Instanz sowie Einsparungen durch die Vermeidung von Code , der die Singleness erzwingt - was auch bedeutet, dass Sie den Code nicht opfern Möglichkeit, eine zweite Instanz zu erstellen, falls sich Ihre Anforderungen ändern.
CHao

4
"Wenn Sie nur ein Objekt eines Typs im System haben müssen" - "... und dieses Objekt niemals in einem Komponententest verspotten möchten."
Cygon

72

Singletons geben Ihnen die Möglichkeit, zwei schlechte Eigenschaften in einer Klasse zu kombinieren. Das ist in fast jeder Hinsicht falsch.

Ein Singleton gibt Ihnen:

  1. Globaler Zugriff auf ein Objekt und
  2. Eine Garantie, dass niemals mehr als ein Objekt dieses Typs erstellt werden kann

Nummer eins ist unkompliziert. Globals sind im Allgemeinen schlecht. Wir sollten Objekte niemals global zugänglich machen, es sei denn, wir brauchen sie wirklich .

Nummer zwei klingt vielleicht nach Sinn, aber lassen Sie uns darüber nachdenken. Wann haben Sie das letzte Mal ** versehentlich * ein neues Objekt erstellt, anstatt auf ein vorhandenes zu verweisen? Da dies mit C ++ gekennzeichnet ist, verwenden wir ein Beispiel aus dieser Sprache. Schreibst du oft versehentlich

std::ostream os;
os << "hello world\n";

Wenn du schreiben wolltest

std::cout << "hello world\n";

Natürlich nicht. Wir brauchen keinen Schutz gegen diesen Fehler, weil diese Art von Fehler einfach nicht auftritt. Wenn dies der Fall ist, ist die richtige Antwort, nach Hause zu gehen und 12 bis 20 Stunden zu schlafen und zu hoffen, dass Sie sich besser fühlen.

Wenn nur ein Objekt benötigt wird, erstellen Sie einfach eine Instanz. Wenn auf ein Objekt global zugegriffen werden soll, machen Sie es zu einem globalen Objekt. Das heißt aber nicht, dass es unmöglich sein sollte, andere Instanzen davon zu erstellen.

Die Einschränkung "Nur eine Instanz ist möglich" schützt uns nicht wirklich vor wahrscheinlichen Fehlern. Es macht es jedoch sehr schwierig, unseren Code umzugestalten und zu warten. Denn oft stellen wir später fest, dass wir mehr als eine Instanz benötigt haben. Wir tun mehr als eine Datenbank, wir haben mehr als einen Konfigurationsobjekt, wir mehrere Logger wollen. Unsere Komponententests möchten möglicherweise in der Lage sein, diese Objekte bei jedem Test zu erstellen und neu zu erstellen, um ein allgemeines Beispiel zu nennen.

Ein Singleton sollte also genau dann verwendet werden, wenn wir beide Eigenschaften benötigen, die er bietet: Wenn wir einen globalen Zugriff benötigen (was selten ist, da Globals im Allgemeinen nicht empfohlen werden) und wir müssen verhindern, dass jemals jemand mehr als eine Instanz von a erstellt Klasse (was für mich wie ein Designproblem klingt). Der einzige Grund, den ich dafür sehen kann, ist, dass das Erstellen von zwei Instanzen unseren Anwendungsstatus beschädigen würde - wahrscheinlich, weil die Klasse eine Reihe statischer Mitglieder oder eine ähnliche Albernheit enthält. In diesem Fall besteht die offensichtliche Antwort darin, diese Klasse zu reparieren. Es sollte nicht davon abhängen, die einzige Instanz zu sein.

Wenn Sie globalen Zugriff auf ein Objekt benötigen, machen Sie es zu einem globalen Objekt std::cout. Beschränken Sie jedoch nicht die Anzahl der Instanzen, die erstellt werden können.

Wenn Sie die Anzahl der Instanzen einer Klasse unbedingt auf nur eine beschränken müssen und das Erstellen einer zweiten Instanz niemals sicher gehandhabt werden kann, erzwingen Sie dies. Aber machen Sie es nicht auch global zugänglich.

Wenn Sie beide Eigenschaften benötigen, dann 1) machen Sie es zu einem Singleton und 2) lassen Sie mich wissen, wofür Sie das brauchen, weil es mir schwer fällt, mir einen solchen Fall vorzustellen.


3
oder Sie könnten es zu einem globalen machen und nur einen der Nachteile eines Singletons bekommen. Mit dem Singleton würden Sie sich gleichzeitig auf eine Instanz dieser Datenbankklasse beschränken. Warum das tun? Oder Sie könnten sich ansehen, warum Sie so viele Abhängigkeiten haben, dass die Instanziierungsliste "wirklich lang" wird. Sind sie alle notwendig? Sollten einige von ihnen an andere Komponenten delegiert werden? Vielleicht könnten einige von ihnen in einer Struktur zusammengefasst werden, damit wir sie als ein einziges Argument weitergeben können. Es gibt viele Lösungen, alle besser als Singletons.
Jalf

6
Ja, ein Singleton könnte dort gerechtfertigt sein. Aber ich denke, Sie haben gerade bewiesen, dass dies nur in ziemlich exotischen Fällen notwendig ist. Die meiste Software befasst sich nicht mit Schneeräumhardware. Aber ich bin immer noch nicht überzeugt. Ich bin damit einverstanden, dass Sie in Ihrer tatsächlichen Bewerbung nur eine davon möchten. Aber was ist mit Ihren Unit-Tests? Jeder von ihnen sollte isoliert ausgeführt werden, daher sollten sie idealerweise ihren eigenen SpreaderController erstellen - was mit einem Singleton schwer zu tun ist. Warum sollten Ihre Mitarbeiter überhaupt mehrere Instanzen erstellen? Ist das ein realistisches Szenario zum Schutz?
Jalf

3
Und ein Punkt, den Sie verpasst haben, ist, dass Ihre letzten beiden Beispiele zwar die Einschränkung "nur eine Instanz" rechtfertigen, aber nichts tun, um die "global zugängliche" zu rechtfertigen. Warum um alles in der Welt sollte die gesamte Codebasis auf die Verwaltungseinheit Ihres Telefonschalters zugreifen können? In einem Singleton geht es darum, Ihnen beide Eigenschaften zu geben . Wenn Sie nur den einen oder anderen benötigen, sollten Sie keinen Singleton verwenden.
Jalf

2
@ jalf - Mein Ziel war es, Ihnen ein Beispiel zu geben, wo Singleton in freier Wildbahn nützlich ist, da Sie sich keine vorstellen können. Ich denke, Sie sehen nicht zu oft, um es auf Ihre aktuelle Arbeit anzuwenden. Ich habe nur aus Geschäftsanwendungen in die Schneepflugprogrammierung gewechselt, weil ich damit Singleton verwenden konnte. :) j / k Ich stimme Ihrer Prämisse zu, dass es bessere Möglichkeiten gibt, diese Dinge zu tun. Sie haben mir viel zu denken gegeben. Danke für die Diskussion!
J. Polfer

2
Die Verwendung des Singleton ( AHEM! ) "Musters", um zu verhindern, dass Personen mehr Instanzen instanziieren, ist einfach alt und dumm, nur um zu verhindern, dass Personen dies versehentlich tun. Wenn ich eine lokale Variable foo1 vom Typ Foo in meiner kleinen Funktion habe und nur eine in der Funktion haben möchte, mache ich mir keine Sorgen, dass jemand eine zweite Foo-Variable foo2 erstellen und diese anstelle der ursprünglichen verwenden wird.
Thomas Eding

36

Das Problem mit Singletons ist nicht ihre Implementierung. Es ist so, dass sie zwei verschiedene Konzepte miteinander verbinden, von denen keines offensichtlich wünschenswert ist.

1) Singletons bieten einen globalen Zugriffsmechanismus auf ein Objekt. Obwohl sie in Sprachen ohne eine genau definierte Initialisierungsreihenfolge möglicherweise geringfügig threadsicherer oder geringfügig zuverlässiger sind, ist diese Verwendung immer noch das moralische Äquivalent einer globalen Variablen. Es ist eine globale Variable, die in einer umständlichen Syntax (z. B. foo :: get_instance () anstelle von g_foo) verkleidet ist, aber genau denselben Zweck erfüllt (ein einzelnes Objekt, auf das über das gesamte Programm zugegriffen werden kann) und genau dieselben Nachteile aufweist.

2) Singletons verhindern mehrere Instanziierungen einer Klasse. Es ist selten, IME, dass diese Art von Feature in eine Klasse gebacken wird. Es ist normalerweise eine viel kontextuellere Sache; Viele der Dinge, die als Eins-und-Nur-Eins angesehen werden, sind wirklich nur Zufällige. IMO ist eine geeignetere Lösung, nur eine Instanz zu erstellen - bis Sie feststellen, dass Sie mehr als eine Instanz benötigen.


6
Einverstanden. In der realen Welt können zwei Fehler nach Ansicht einiger ein Recht machen. Beim Programmieren führt das Mischen zweier schlechter Ideen jedoch nicht zu einer guten.
Jalf

27

Eine Sache mit Mustern: Verallgemeinern Sie nicht . Sie haben alle Fälle, in denen sie nützlich sind und wenn sie versagen.

Singleton kann böse sein, wenn Sie den Code testen müssen. Sie stecken im Allgemeinen in einer Instanz der Klasse fest und können wählen, ob Sie eine Tür im Konstruktor öffnen oder eine Methode zum Zurücksetzen des Status usw. verwenden möchten.

Ein anderes Problem ist, dass der Singleton tatsächlich nichts anderes als eine verkleidete globale Variable ist . Wenn Sie zu viel globalen gemeinsamen Status über Ihr Programm haben, gehen die Dinge tendenziell zurück, wir alle wissen es.

Dies kann die Abhängigkeitsverfolgung erschweren . Wenn alles von Ihrem Singleton abhängt, ist es schwieriger, es zu ändern, in zwei Teile zu teilen usw. Sie bleiben im Allgemeinen dabei. Dies beeinträchtigt auch die Flexibilität. Untersuchen Sie ein Abhängigkeitsinjektions- Framework, um dieses Problem zu beheben.


8
Nein, ein Singleton ist viel mehr als eine verkleidete globale Variable. Das macht es besonders schlimm. Es verbindet die globale-ness mit einem anderen Konzept (die in der Regel schlecht ist) , das ist auch schlecht (das nicht den Programmierer instantiate eine Klasse zu lassen , wenn er entscheidet , er eine Instanz benötigt) Sie werden oft verwendet als globale Variablen, ja. Und dann ziehen sie auch den anderen bösen Nebeneffekt mit sich und lähmen die Codebasis.
Jalf

7
Es sollte auch beachtet werden, dass Singletons nicht öffentlich zugänglich sein müssen. Ein Singleton kann sich sehr gut in der Bibliothek befinden und dem Benutzer niemals zugänglich sein. Sie sind also nicht unbedingt "global" in diesem Sinne.
Steven Evers

1
+1, um darauf hinzuweisen, wie schmerzhaft Singletons beim Testen sind.
DevSolar

1
@jalf Es ist keine schlechte Sache, jemandem nicht zu erlauben, mehr als eine Instanz einer Klasse zu erstellen. Wenn es wirklich immer nur eine Instanz der instanziierten Klasse geben soll, die die Anforderung erzwingt. Wenn jemand später entscheidet, dass er eine andere Instanz erstellen muss, sollte er diese umgestalten, da es sich in erster Linie nie um einen Singleton handeln sollte.
William

2
@ William: und ich musste von Zeit zu Zeit mehrere Logger haben. Sie streiten sich nicht für einen Singleton, sondern für einen einfachen alten Einheimischen. Sie möchten wissen, dass ein Logger immer verfügbar ist. Dafür ist ein Global da. Sie müssen nicht wissen, dass kein anderer Logger jemals instanziiert werden kann , was ein Singleton erzwingt. (Versuchen Sie, Komponententests für Ihren Logger zu schreiben - das ist viel einfacher, wenn Sie es nach Bedarf erstellen und zerstören können, und das ist mit einem Singleton nicht möglich)
Jalf

13

Mit Singletons können Sie im Grunde genommen einen komplexen globalen Status in Sprachen haben, der es ansonsten schwierig oder unmöglich macht, komplexe globale Variablen zu haben.

Insbesondere Java verwendet Singletons als Ersatz für globale Variablen, da alles in einer Klasse enthalten sein muss. Am nächsten an globalen Variablen sind öffentliche statische Variablen, die verwendet werden können, als ob sie global wärenimport static

C ++ hat globale Variablen, aber die Reihenfolge, in der Konstruktoren globaler Klassenvariablen aufgerufen werden, ist undefiniert. Mit einem Singleton können Sie die Erstellung einer globalen Variablen verschieben, bis diese Variable zum ersten Mal benötigt wird.

Sprachen wie Python und Ruby verwenden nur sehr wenige Singletons, da Sie stattdessen globale Variablen innerhalb eines Moduls verwenden können.

Wann ist es also gut / schlecht, einen Singleton zu verwenden? Ziemlich genau, wann es gut / schlecht wäre, eine globale Variable zu verwenden.


Wann ist eine globale Variable "gut"? Manchmal sind sie die beste Lösung für ein Problem, aber sie sind nie "gut".
DevSolar

1
Globale Variable ist gut, wenn sie überall verwendet wird und alles Zugriff darauf hat. Eine Implementierung einer Einzelzustands-Turingmaschine kann einen Singleton verwenden.
Lee Louviere

Ich mag die Indirektionsebene in dieser Antwort: "Wenn es gut / schlecht wäre, eine globale zu verwenden". Sowohl DevSolar als auch Lee Louviere erhalten den Wert, dem sie zustimmen, obwohl zum Zeitpunkt der Beantwortung nicht bekannt sein konnte, wer einen Kommentar abgeben würde.
Praxeolitic

6

Modernes C ++ - Design von Alexandrescu verfügt über einen thread-sicheren, vererbbaren generischen Singleton.

Für meinen 2p-Wert denke ich, dass es wichtig ist, definierte Lebensdauern für Ihre Singletons zu haben (wenn es absolut notwendig ist, sie zu verwenden). Normalerweise lasse ich die statische get()Funktion nichts instanziieren und überlasse die Einrichtung und Zerstörung einem bestimmten Abschnitt der Hauptanwendung. Dies hilft dabei, Abhängigkeiten zwischen Singletons hervorzuheben - aber wie oben betont, ist es am besten, sie nach Möglichkeit zu vermeiden.


6
  • Wie implementiere ich einen Singleton richtig?

Es gibt ein Problem, das ich noch nie erwähnt habe, etwas, auf das ich bei einem früheren Job gestoßen bin. Wir hatten C ++ - Singletons, die von DLLs gemeinsam genutzt wurden, und die üblichen Mechanismen, um sicherzustellen, dass eine einzelne Instanz einer Klasse funktioniert, funktionieren einfach nicht. Das Problem ist, dass jede DLL zusammen mit der EXE-Datei einen eigenen Satz statischer Variablen erhält. Wenn Ihre Funktion get_instance inline oder Teil einer statischen Bibliothek ist, wird jede DLL mit einer eigenen Kopie des "Singleton" abgeschlossen.

Die Lösung besteht darin, sicherzustellen, dass der Singleton-Code nur in einer DLL oder EXE definiert ist, oder einen Singleton-Manager mit diesen Eigenschaften zu erstellen, um Instanzen zu parzellieren.


10
Yo dawg, ich habe gehört, dass du Singletons magst, also habe ich einen Singleton für deinen Singleton gemacht, damit du gegen Muster vorgehen kannst, während du gegen Muster bist.
Eva

@Eva, ja so etwas. Ich habe das Problem nicht geschaffen, ich musste es nur irgendwie zum Laufen bringen.
Mark Ransom

5

Das erste Beispiel ist nicht threadsicher. Wenn zwei Threads gleichzeitig getInstance aufrufen, ist diese Statik eine PITA. Eine Form von Mutex würde helfen.


Ja, das ist in den obigen Kommentaren vermerkt: * Einschränkung: Single Threaded Design * Siehe: aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf * Für Probleme im Zusammenhang mit dem Sperren in Multithread-Anwendungen
Martin York,

Der klassische Singleton mit nur getInstance als statischer Methode und Instanzmethoden für andere Operationen kann niemals threadsicher gemacht werden. (Nun, es sei denn, Sie machen es zu einer Single-pro-Thread-Tonne mit lokalem Thread-Speicher ...)
Tobi

sogar in c ++ 11 oder höher?
hg_git

5

Wie andere angemerkt haben, umfassen die Hauptnachteile von Singletons die Unfähigkeit, sie zu erweitern, und den Verlust der Fähigkeit, mehr als eine Instanz zu instanziieren, z. B. zu Testzwecken.

Einige nützliche Aspekte von Singletons:

  1. faule oder vorab Instanziierung
  2. Praktisch für ein Objekt, das Setup und / oder Status erfordert

Sie müssen jedoch keinen Singleton verwenden, um diese Vorteile zu erzielen. Sie können ein normales Objekt schreiben, das die Arbeit erledigt, und dann über eine Factory (ein separates Objekt) darauf zugreifen lassen. Die Fabrik kann sich darum kümmern, nur eine zu instanziieren und sie bei Bedarf wiederzuverwenden usw. Wenn Sie auf eine Schnittstelle anstatt auf eine konkrete Klasse programmieren, kann die Factory Strategien verwenden, dh Sie können verschiedene Implementierungen der Schnittstelle ein- und ausschalten.

Schließlich eignet sich eine Fabrik für Abhängigkeitsinjektionstechnologien wie Spring usw.


3

Singletons sind praktisch, wenn beim Initialisieren und Objektieren viel Code ausgeführt wird. Wenn Sie beispielsweise iBatis verwenden, wenn Sie ein Persistenzobjekt einrichten, muss es alle Konfigurationen lesen, die Karten analysieren, sicherstellen, dass alles korrekt ist usw., bevor Sie zu Ihrem Code gelangen.

Wenn Sie dies jedes Mal tun, wird die Leistung stark beeinträchtigt. Wenn Sie es in einem Singleton verwenden, nehmen Sie diesen Treffer einmal und dann müssen alle nachfolgenden Anrufe es nicht tun.


Das Prototype Pattern macht dies ebenfalls und ist flexibler. Sie können es auch verwenden, wenn Ihr Client viele Instanzen Ihrer teuren Klasse erstellt, aber nur eine begrenzte Anzahl von ihnen tatsächlich einen anderen Status hat. Zum Beispiel Tetronimos in Tetris.
Eva

3

Der wirkliche Untergang von Singletons ist, dass sie das Erbe brechen. Sie können keine neue Klasse ableiten, um erweiterte Funktionen zu erhalten, es sei denn, Sie haben Zugriff auf den Code, auf den der Singleton verweist. Abgesehen von der Tatsache, dass der Singleton Ihren Code eng miteinander verbindet (durch ein Strategiemuster korrigierbar ... auch bekannt als Abhängigkeitsinjektion), verhindert er auch, dass Sie Abschnitte des Codes für die Revision schließen (gemeinsam genutzte Bibliotheken).

Daher sind auch die Beispiele für Logger oder Thread-Pools ungültig und sollten durch Strategien ersetzt werden.


Logger selbst sollten keine Singletons sein. Das allgemeine "Broadcast" -Nachrichtensystem sollte sein. Logger selbst sind Abonnenten der Broadcast-Nachrichten.
CashCow

Thread-Pools sollten auch keine Singletons sein. Die allgemeine Frage ist, würden Sie jemals mehr als einen von ihnen wollen? Ja. Als ich sie das letzte Mal benutzte, hatten wir 3 verschiedene Thread-Pools in einer Anwendung.
CashCow

3

Die meisten Menschen verwenden Singletons, wenn sie versuchen, sich bei der Verwendung einer globalen Variablen wohl zu fühlen. Es gibt legitime Verwendungen, aber die meiste Zeit, wenn Menschen sie verwenden, ist die Tatsache, dass es nur eine Instanz geben kann, nur eine triviale Tatsache im Vergleich zu der Tatsache, dass sie global zugänglich ist.


3

Da mit einem Singleton nur eine Instanz erstellt werden kann, wird die Instanzreplikation effektiv gesteuert. Zum Beispiel würden Sie nicht mehrere Instanzen einer Suche benötigen - eine Morse-Lookup-Map zum Beispiel, daher ist es passend, sie in eine Singleton-Klasse zu verpacken. Und nur weil Sie eine einzelne Instanz der Klasse haben, bedeutet dies nicht, dass Sie auch auf die Anzahl der Verweise auf diese Instanz beschränkt sind. Sie können Aufrufe (um Threading-Probleme zu vermeiden) an die Instanz in die Warteschlange stellen und erforderliche Änderungen vornehmen. Ja, die allgemeine Form eines Singletons ist global öffentlich. Sie können das Design sicherlich ändern, um einen Singleton mit eingeschränkterem Zugriff zu erstellen. Ich habe das noch nie müde gemacht, aber ich weiß, dass es möglich ist. Und allen, die kommentiert haben, dass das Singleton-Muster absolut böse ist, sollten Sie Folgendes wissen:



2

Im Folgenden finden Sie den besseren Ansatz zum Implementieren eines thread-sicheren Singleton-Musters mit Freigabe des Speichers im Destruktor selbst. Ich denke jedoch, dass der Destruktor optional sein sollte, da die Singleton-Instanz beim Beenden des Programms automatisch zerstört wird:

#include<iostream>
#include<mutex>

using namespace std;
std::mutex mtx;

class MySingleton{
private:
    static MySingleton * singletonInstance;
    MySingleton();
    ~MySingleton();
public:
    static MySingleton* GetInstance();
    MySingleton(const MySingleton&) = delete;
    const MySingleton& operator=(const MySingleton&) = delete;
    MySingleton(MySingleton&& other) noexcept = delete;
    MySingleton& operator=(MySingleton&& other) noexcept = delete;
};

MySingleton* MySingleton::singletonInstance = nullptr;
MySingleton::MySingleton(){ };
MySingleton::~MySingleton(){
    delete singletonInstance;
};

MySingleton* MySingleton::GetInstance(){
    if (singletonInstance == NULL){
        std::lock_guard<std::mutex> lock(mtx);
        if (singletonInstance == NULL)
            singletonInstance = new MySingleton();
    }
    return singletonInstance;
}

In Bezug auf die Situationen, in denen Singleton-Klassen verwendet werden müssen, kann Folgendes gelten: Wenn wir den Status der Instanz während der Ausführung des Programms beibehalten möchten, wenn wir in das Ausführungsprotokoll einer Anwendung schreiben möchten, in der nur eine Instanz der Datei erforderlich ist verwendet werden .... und so weiter. Es ist bemerkenswert, wenn jemand eine Optimierung in meinem obigen Code vorschlagen kann.


2
Das ist definitiv nicht besser. 1: Sie definieren die Besitzersemantik nicht mithilfe eines Zeigers. Sie sollten in C ++ niemals Zeiger verwenden, es sei denn, Sie haben sich darauf vorbereitet, sie zu verwalten. 2: Ihre Verwendung der doppelt überprüften Verriegelung ist veraltet und es gibt viel bessere moderne Möglichkeiten, dies zu tun. 3: Ihre Kommentare zur Zerstörung sind naiv. Die Rückgewinnung von Speicher ist nicht der Punkt, an dem der Destruktor verwendet wird, sondern die Bereinigung. Vorschläge für eine bessere Version: Schauen Sie sich die Frage an. Die dort vorgestellte Version ist schon viel besser.
Martin York

1

Ich benutze Singletons als Interviewtest.

Wenn ich einen Entwickler auffordere, einige Entwurfsmuster zu benennen, werden sie nicht eingestellt, wenn sie nur Singleton nennen können.


45
Harte und schnelle Regeln für die Einstellung lassen Sie eine Vielzahl potenzieller Mitarbeiter verpassen.
Karl

13
Es gibt eine große Vielfalt von Idioten. Das bedeutet nicht, dass sie für die Einstellung in Betracht gezogen werden sollten. Wenn jemand überhaupt keine Entwurfsmuster erwähnen kann, wäre er meiner Meinung nach jemandem vorzuziehen, der den Singleton kennt, und keinen anderen Mustern.
Jalf

3
Für das Rekordbuch - meine Antwort war ironisch. In meinem eigentlichen Interviewprozess versuche ich zu beurteilen, ob wir jemanden in C ++ unterrichten müssen und wie schwierig das sein wird. Einige meiner Lieblingskandidaten sind Leute, die C ++ NICHT in- und auswendig kennen, aber ich konnte mit ihnen ein großartiges Gespräch darüber führen.
Matt Cruikshank

4
Abstimmung nach unten. Aus meiner persönlichen Erfahrung kann der Programmierer möglicherweise keine anderen Muster als Singleton benennen, aber das bedeutet nicht, dass er Singletons verwendet. Persönlich habe ich Singletons in meinem Code verwendet, bevor ich jemals von ihnen gehört habe (ich habe sie "intelligentere Globals" genannt - ich wusste, was das Globale ist). Als ich von ihnen erfuhr, als ich von ihren Vor- und Nachteilen erfuhr, hörte ich auf, sie zu benutzen. Plötzlich wurden Unit-Tests für mich viel interessanter, als ich aufhörte ... Hat mich das zu einem schlechteren Programmierer gemacht? Pfff ...
Paulius

3
Ich stimme auch für die Unsinnsfrage "Name einige Designmuster". Beim Entwerfen geht es darum zu verstehen, wie Entwurfsmuster angewendet werden, und nicht nur darum, ihre Namen abzuspulen. Ok, das mag keine Ablehnung rechtfertigen, aber diese Antwort ist trollig.
CashCow

0

Anti-Usage:

Ein Hauptproblem bei übermäßiger Singleton-Nutzung besteht darin, dass das Muster ein einfaches Erweitern und Austauschen alternativer Implementierungen verhindert. Der Klassenname ist überall dort fest codiert, wo der Singleton verwendet wird.


Aus zwei Gründen herabgestuft: 1. Singleton kann intern polymorphe Instanzen verwenden (z. B. verwendet Global Logger polymorphe Zielstrategien). 2. Es kann typedef für Singleton-Namen geben, sodass der Code tatsächlich von typedef abhängt.
Topright Gamedev

Am Ende habe ich meine Version eines Singletons so erstellt, dass sie mithilfe des merkwürdig wiederkehrenden Vorlagenmusters erweiterbar ist.
Zachary Kraus

0

Ich denke, dies ist die robusteste Version für C #:

using System;
using System.Collections;
using System.Threading;

namespace DoFactory.GangOfFour.Singleton.RealWorld
{

  // MainApp test application

  class MainApp
  {
    static void Main()
    {
      LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b4 = LoadBalancer.GetLoadBalancer();

      // Same instance?
      if (b1 == b2 && b2 == b3 && b3 == b4)
      {
        Console.WriteLine("Same instance\n");
      }

      // All are the same instance -- use b1 arbitrarily
      // Load balance 15 server requests
      for (int i = 0; i < 15; i++)
      {
        Console.WriteLine(b1.Server);
      }

      // Wait for user
      Console.Read();    
    }
  }

  // "Singleton"

  class LoadBalancer
  {
    private static LoadBalancer instance;
    private ArrayList servers = new ArrayList();

    private Random random = new Random();

    // Lock synchronization object
    private static object syncLock = new object();

    // Constructor (protected)
    protected LoadBalancer()
    {
      // List of available servers
      servers.Add("ServerI");
      servers.Add("ServerII");
      servers.Add("ServerIII");
      servers.Add("ServerIV");
      servers.Add("ServerV");
    }

    public static LoadBalancer GetLoadBalancer()
    {
      // Support multithreaded applications through
      // 'Double checked locking' pattern which (once
      // the instance exists) avoids locking each
      // time the method is invoked
      if (instance == null)
      {
        lock (syncLock)
        {
          if (instance == null)
          {
            instance = new LoadBalancer();
          }
        }
      }

      return instance;
    }

    // Simple, but effective random load balancer

    public string Server
    {
      get
      {
        int r = random.Next(servers.Count);
        return servers[r].ToString();
      }
    }
  }
}

Hier ist die .NET-optimierte Version :

using System;
using System.Collections;

namespace DoFactory.GangOfFour.Singleton.NETOptimized
{

  // MainApp test application

  class MainApp
  {

    static void Main()
    {
      LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
      LoadBalancer b4 = LoadBalancer.GetLoadBalancer();

      // Confirm these are the same instance
      if (b1 == b2 && b2 == b3 && b3 == b4)
      {
        Console.WriteLine("Same instance\n");
      }

      // All are the same instance -- use b1 arbitrarily
      // Load balance 15 requests for a server
      for (int i = 0; i < 15; i++)
      {
        Console.WriteLine(b1.Server);
      }

      // Wait for user
      Console.Read();    
    }
  }

  // Singleton

  sealed class LoadBalancer
  {
    // Static members are lazily initialized.
    // .NET guarantees thread safety for static initialization
    private static readonly LoadBalancer instance =
      new LoadBalancer();

    private ArrayList servers = new ArrayList();
    private Random random = new Random();

    // Note: constructor is private.
    private LoadBalancer()
    {
      // List of available servers
      servers.Add("ServerI");
      servers.Add("ServerII");
      servers.Add("ServerIII");
      servers.Add("ServerIV");
      servers.Add("ServerV");
    }

    public static LoadBalancer GetLoadBalancer()
    {
      return instance;
    }

    // Simple, but effective load balancer
    public string Server
    {
      get
      {
        int r = random.Next(servers.Count);
        return servers[r].ToString();
      }
    }
  }
}

Sie finden dieses Muster unter dotfactory.com .


3
Sie können die Teile entfernen, die für Singletons nicht spezifisch relevant sind, um den Code leichter lesbar zu machen.
Martin York

Außerdem ist Ihre erste Version aufgrund einer möglichen Neuordnung von Lesen und Schreiben nicht threadsicher. Siehe stackoverflow.com/questions/9666/…
Thomas Danecker

5
Äh ... falsche Sprache? Die Frage ist ziemlich offensichtlich mit C ++ gekennzeichnet .
DevSolar

0

Das Meyers-Singleton-Muster funktioniert die meiste Zeit gut genug, und manchmal lohnt es sich nicht, nach etwas Besserem zu suchen. Solange der Konstruktor niemals wirft und es keine Abhängigkeiten zwischen Singletons gibt.

Ein Singleton ist eine Implementierung für ein global zugängliches Objekt (GAO von nun an), obwohl nicht alle GAOs Singletons sind.

Logger selbst sollten keine Singletons sein, aber die Mittel zum Protokollieren sollten idealerweise global zugänglich sein, um zu entkoppeln, wo die Protokollnachricht von wo generiert wird oder wie sie protokolliert wird.

Lazy-Loading / Lazy-Evaluierung ist ein anderes Konzept, und Singleton implementiert dies normalerweise auch. Es bringt viele eigene Probleme mit sich, insbesondere die Thread-Sicherheit und Probleme, wenn es mit Ausnahmen fehlschlägt, so dass sich herausstellt, dass eine gute Idee zu dieser Zeit doch nicht so toll ist. (Ein bisschen wie die COW-Implementierung in Strings).

In diesem Sinne können GOAs wie folgt initialisiert werden:

namespace {

T1 * pt1 = NULL;
T2 * pt2 = NULL;
T3 * pt3 = NULL;
T4 * pt4 = NULL;

}

int main( int argc, char* argv[])
{
   T1 t1(args1);
   T2 t2(args2);
   T3 t3(args3);
   T4 t4(args4);

   pt1 = &t1;
   pt2 = &t2;
   pt3 = &t3;
   pt4 = &t4;

   dostuff();

}

T1& getT1()
{
   return *pt1;
}

T2& getT2()
{
   return *pt2;
}

T3& getT3()
{
  return *pt3;
}

T4& getT4()
{
  return *pt4;
}

Es muss nicht so grob gemacht werden, und in einer geladenen Bibliothek, die Objekte enthält, möchten Sie wahrscheinlich einen anderen Mechanismus, um deren Lebensdauer zu verwalten. (Legen Sie sie in ein Objekt, das Sie beim Laden der Bibliothek erhalten).

Wann benutze ich Singletons? Ich habe sie für zwei Dinge verwendet: Eine Singleton-Tabelle, die angibt, welche Bibliotheken mit dlopen geladen wurden. Ein Nachrichtenhandler, den Logger abonnieren und an den Sie Nachrichten senden können. Speziell für Signalhandler erforderlich.


0

Ich verstehe immer noch nicht, warum ein Singleton global sein muss.

Ich wollte einen Singleton erstellen, in dem ich eine Datenbank innerhalb der Klasse als private konstante statische Variable versteckte und Klassenfunktionen erstellte, die die Datenbank verwenden, ohne die Datenbank jemals dem Benutzer zugänglich zu machen.

Ich verstehe nicht, warum diese Funktionalität schlecht wäre.


Ich verstehe nicht, warum du denkst, dass es global sein muss.
Martin York

Laut diesem Thread sagten alle, ein Singleton müsse global sein
Zachary Kraus

1
Nein. Der Thread zeigt an, dass ein Singelton einen globalen Status hat. Nicht dass es eine globale Variable wäre. Die von Ihnen vorgeschlagene Lösung hat einen globalen Status. Die von Ihnen vorgeschlagene Lösung verwendet auch eine globale Variable. Ein statisches Mitglied einer Klasse ist ein Objekt der "statischen Speicherdauer". Eine globale Variable ist ein Objekt der "statischen Speicherdauer". Somit sind die beiden im Grunde dasselbe mit leicht unterschiedlichen Semantiken / Bereichen.
Martin York

Eine private statische Variable ist also aufgrund der "statischen Speicherdauer" immer noch global?
Zachary Kraus

1
Hinweis: Sie haben mein absichtlich nicht angegebenes Bit verpasst. Ihr Entwurf, ein statisches "privates" Mitglied zu verwenden, ist nicht so schlecht wie ein Singelton. Weil es keinen "globalen veränderlichen Zustand" einführt. Aber es ist auch kein Singleton. Ein Singleton ist eine Klasse, die so konzipiert ist, dass nur eine Instanz des Objekts vorhanden sein kann. Was Sie vorschlagen, ist ein einzelner gemeinsamer Status für alle Objekte einer Klasse. Anderes Konzept.
Martin York

0

Ich finde sie nützlich, wenn ich eine Klasse habe, die viel Speicher enthält. Zum Beispiel habe ich in einem kürzlich durchgeführten Spiel, an dem ich gearbeitet habe, eine Einflusskartenklasse, die eine Sammlung sehr großer Arrays zusammenhängenden Speichers enthält. Ich möchte, dass alle beim Start zugewiesen, alle beim Herunterfahren freigegeben werden und ich möchte definitiv nur eine Kopie davon. Ich muss auch von vielen Orten darauf zugreifen. Ich finde das Singleton-Muster in diesem Fall sehr nützlich.

Ich bin mir sicher, dass es andere Lösungen gibt, aber ich finde diese sehr nützlich und einfach zu implementieren.


0

Wenn Sie derjenige sind, der das Singleton erstellt hat und es verwendet, machen Sie es nicht als Singleton (es hat keinen Sinn, weil Sie die Singularität des Objekts steuern können, ohne es zu einem Singleton zu machen), aber es ist sinnvoll, wenn Sie ein Entwickler von a sind Bibliothek und Sie möchten Ihren Benutzern nur ein Objekt zur Verfügung stellen (in diesem Fall sind Sie derjenige, der den Singleton erstellt hat, aber Sie sind nicht der Benutzer).

Singletons sind Objekte. Verwenden Sie sie daher als Objekte. Viele Benutzer greifen direkt auf Singletons zu, indem sie die Methode aufrufen, die sie zurückgibt. Dies ist jedoch schädlich, da Sie Ihrem Code mitteilen, dass das Objekt Singleton ist. Ich bevorzuge die Verwendung von Singletons als Objekte. Ich übergebe sie Durch den Konstruktor und ich verwende sie als gewöhnliche Objekte. Auf diese Weise weiß Ihr Code nicht, ob diese Objekte Singletons sind oder nicht. Dies macht die Abhängigkeiten klarer und hilft ein wenig beim Refactoring ...


-1

In Desktop-Apps (ich weiß, nur wir Dinosaurier schreiben diese noch!) Sind sie unerlässlich, um relativ unveränderte globale Anwendungseinstellungen zu erhalten - die Benutzersprache, der Pfad zu Hilfedateien, Benutzereinstellungen usw., die sich sonst in jeder Klasse und jedem Dialog verbreiten müssten .

Bearbeiten - natürlich sollten diese schreibgeschützt sein!


Aber das wirft die Frage auf; Warum die Benutzersprache und der Pfad zur Hilfedatei müssen Instanzen Methode überhaupt ?
DrPizza

2
Dafür haben wir Globale. Es ist nicht nötig, sie zu Singletons zu machen
Jalf

Globale Variablen - wie serialisieren Sie sie dann aus der Registrierung / Datenbank? Weltklasse - wie stellen Sie dann sicher, dass es nur eine davon gibt?
Martin Beckett

@mgb: Sie serialisieren sie, indem Sie Werte aus der Registrierung / Datenbank lesen und in den globalen Variablen speichern (dies sollte wahrscheinlich oben in Ihrer Hauptfunktion erfolgen). Sie stellen sicher, dass es nur ein Objekt einer Klasse gibt, indem Sie nur ein Objekt der Klasse erstellen ... wirklich ... ist es so schwer, "new \ + global_class_name" zu "grep -rn". ? Ja wirklich?
Paulius

7
@mgb: Warum um alles in der Welt sollte ich sicherstellen, dass es nur einen gibt? Ich muss nur wissen, dass eine Instanz immer die aktuellen Einstellungen darstellt. Aber es gibt keinen Grund, warum ich keine anderen Einstellungsobjekte in der Nähe haben sollte. Vielleicht eine für "die Einstellungen, die der Benutzer gerade definiert, aber noch nicht angewendet hat", zum Beispiel. Oder eine für "die Konfiguration, die der Benutzer zuvor gespeichert hat, damit er später zu ihnen zurückkehren kann". Oder eine für jeden Ihrer Unit-Tests.
Jalf

-1

Eine weitere Implementierung

class Singleton
{
public:
    static Singleton& Instance()
    {
        // lazy initialize
        if (instance_ == NULL) instance_ = new Singleton();

        return *instance_;
    }

private:
    Singleton() {};

    static Singleton *instance_;
};

3
Das ist wirklich schrecklich. Siehe: stackoverflow.com/questions/1008019/c-singleton-design-pattern/… für eine bessere verzögerte Initialisierung und vor allem eine garantierte deterministische Zerstörung.
Martin York

Wenn Sie Zeiger verwenden möchten, Instance()sollten Sie einen Zeiger und keine Referenz zurückgeben. .cppInitialisieren Sie die Instanz in Ihrer Datei auf null : Singleton* Singleton::instance_ = nullptr;. Und Instance()sollte implementiert werden als : if (instance_ == nullptr) instance_ = new Singleton(); return instance_;.
Dennis
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.