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.
:-)
quelle
Antworten:
Sie alle liegen falsch. Lies die Frage. Antworten:
Verwenden Sie einen Singleton, wenn:
Verwenden Sie keinen Singleton, wenn:
So erstellen Sie den besten Singleton:
quelle
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:
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
Wenn du schreiben wolltest
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.
quelle
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.
quelle
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.
quelle
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ären
import 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.
quelle
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.quelle
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.
quelle
Das erste Beispiel ist nicht threadsicher. Wenn zwei Threads gleichzeitig getInstance aufrufen, ist diese Statik eine PITA. Eine Form von Mutex würde helfen.
quelle
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:
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.
quelle
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.
quelle
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.
quelle
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.
quelle
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:
quelle
Aber wenn ich so etwas wie einen Singleton brauche, benutze ich oft einen Schwarz-Zähler , um ihn zu instanziieren.
quelle
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:
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.
quelle
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.
quelle
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.
quelle
Ich denke, dies ist die robusteste Version für C #:
Hier ist die .NET-optimierte Version :
Sie finden dieses Muster unter dotfactory.com .
quelle
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:
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.
quelle
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.
quelle
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.
quelle
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 ...
quelle
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!
quelle
Eine weitere Implementierung
quelle
Instance()
sollten Sie einen Zeiger und keine Referenz zurückgeben..cpp
Initialisieren Sie die Instanz in Ihrer Datei auf null :Singleton* Singleton::instance_ = nullptr;
. UndInstance()
sollte implementiert werden als :if (instance_ == nullptr) instance_ = new Singleton(); return instance_;
.