In letzter Zeit wurde viel über die Probleme bei der Verwendung (und Überbeanspruchung) von Singletons diskutiert. Ich war auch schon früher in meiner Karriere einer dieser Leute. Ich kann jetzt das Problem erkennen, und dennoch gibt es immer noch viele Fälle, in denen ich keine gute Alternative sehe - und nicht viele der Anti-Singleton-Diskussionen bieten wirklich eine.
Hier ist ein reales Beispiel aus einem großen Projekt, an dem ich in letzter Zeit beteiligt war:
Die Anwendung war ein Thick-Client mit vielen separaten Bildschirmen und Komponenten, der große Datenmengen aus einem Serverstatus verwendet, der nicht zu oft aktualisiert wird. Diese Daten wurden im Grunde genommen in einem Singleton "Manager" -Objekt zwischengespeichert - dem gefürchteten "globalen Zustand". Die Idee war, diesen einen Ort in der App zu haben, an dem die Daten gespeichert und synchronisiert werden, und dann können alle neuen Bildschirme, die geöffnet werden, das meiste von dem abfragen, was sie von dort benötigen, ohne wiederholte Anfragen nach verschiedenen unterstützenden Daten vom Server zu stellen. Eine ständige Anfrage an den Server würde zu viel Bandbreite in Anspruch nehmen - und ich spreche von Tausenden von Dollar zusätzlichen Internetrechnungen pro Woche, was inakzeptabel war.
Gibt es einen anderen Ansatz, der hier angemessen sein könnte, als im Grunde ein solches globales Datenmanager-Cache-Objekt zu haben? Dieses Objekt muss offiziell natürlich kein "Singleton" sein, aber es ist konzeptionell sinnvoll, eines zu sein. Was ist hier eine schöne saubere Alternative?
quelle
Antworten:
Hierbei ist es wichtig, zwischen einzelnen Instanzen und dem Singleton-Entwurfsmuster zu unterscheiden .
Einzelne Instanzen sind einfach Realität. Die meisten Apps können jeweils nur mit einer Konfiguration, einer Benutzeroberfläche, einem Dateisystem usw. verwendet werden. Wenn viele Zustände oder Daten gepflegt werden müssen, möchten Sie mit Sicherheit nur eine Instanz haben und diese so lange wie möglich am Leben erhalten.
Das Singleton- Entwurfsmuster ist ein sehr spezifischer Typ einer einzelnen Instanz, und zwar:
Aufgrund dieser speziellen Designauswahl führt das Muster zu mehreren potenziellen Langzeitproblemen:
Keines dieser Symptome ist tatsächlich für einzelne Instanzen endemisch, nur das Singleton-Muster.
Was können Sie stattdessen tun? Verwenden Sie einfach nicht das Singleton-Muster.
Zitat aus der Frage:
Dieses Konzept hat einen Namen, wie Sie andeuten, aber unsicher klingen. Es heißt Cache . Wenn Sie Lust haben, können Sie es als "Offline-Cache" oder nur als Offline-Kopie von Remote-Daten bezeichnen.
Ein Cache muss kein Singleton sein. Es kann brauchen eine einzelne Instanz sein , wenn Sie die gleichen Daten zu holen für mehrere Cache - Instanzen vermeiden möchten; das heißt aber nicht, dass man eigentlich alles allen aussetzen muss .
Das erste, was ich tun würde, ist, die verschiedenen Funktionsbereiche des Caches in separate Schnittstellen aufzuteilen. Angenommen, Sie haben den weltweit schlechtesten YouTube-Klon auf Basis von Microsoft Access erstellt:
Hier haben Sie mehrere Schnittstellen , die die spezifischen Datentypen beschreiben, auf die eine bestimmte Klasse möglicherweise zugreifen muss - Medien, Benutzerprofile und statische Seiten (wie die Startseite). All dies wird von einem Mega-Cache implementiert , aber Sie entwerfen Ihre einzelnen Klassen so, dass sie stattdessen die Schnittstellen akzeptieren, sodass es ihnen egal ist, welche Art von Instanz sie haben. Sie initialisieren die physische Instanz einmal, wenn Ihr Programm gestartet wird, und geben die Instanzen dann einfach über Konstruktoren und öffentliche Eigenschaften weiter (in einen bestimmten Schnittstellentyp umgewandelt).
Dies wird übrigens Abhängigkeitsinjektion genannt ; Sie müssen weder Spring noch einen speziellen IoC-Container verwenden, solange Ihr allgemeines Klassendesign die Abhängigkeiten vom Aufrufer akzeptiert, anstatt sie einzeln zu instanziieren oder auf den globalen Status zu verweisen .
Warum sollten Sie das schnittstellenbasierte Design verwenden? Drei Gründe:
Dies erleichtert das Lesen des Codes. An den Schnittstellen können Sie genau erkennen, von welchen Daten die abhängigen Klassen abhängen.
Wenn Sie feststellen, dass Microsoft Access nicht die beste Wahl für ein Daten-Back-End ist, können Sie es durch etwas Besseres ersetzen - sagen wir, SQL Server.
Falls und wenn Sie , dass SQL Server erkennen nicht die beste Wahl für die Medien ist speziell , können Sie Ihre Implementierung brechen , ohne einen anderen Teil des Systems zu beeinträchtigen . Hier kommt die wahre Kraft der Abstraktion ins Spiel.
Wenn Sie noch einen Schritt weiter gehen möchten, können Sie einen IoC-Container (DI-Framework) wie Spring (Java) oder Unity (.NET) verwenden. Fast jedes DI-Framework führt eine eigene Lebensdauerverwaltung durch und ermöglicht es Ihnen, einen bestimmten Dienst als einzelne Instanz zu definieren (häufig als "Singleton" bezeichnet, dies dient jedoch nur der Vertrautheit). Grundsätzlich ersparen Ihnen diese Frameworks die meiste Affenarbeit beim manuellen Weitergeben von Instanzen, sind jedoch nicht unbedingt erforderlich. Sie benötigen keine speziellen Werkzeuge, um dieses Design zu implementieren.
Der Vollständigkeit halber möchte ich darauf hinweisen, dass das obige Design auch wirklich nicht ideal ist. Wenn Sie mit einem Cache arbeiten (so wie Sie sind), sollten Sie eigentlich eine völlig separate Ebene haben . Mit anderen Worten, ein Design wie dieses:
Dies hat den Vorteil, dass Sie Ihre
Cache
Instanz nicht einmal trennen müssen, wenn Sie sich für eine Umgestaltung entscheiden. Sie können ändern, wie Medien gespeichert werden, indem Sie sie einer alternativen Implementierung von zuführenIMediaRepository
. Wenn Sie sich überlegen, wie dies zusammenpasst, werden Sie feststellen, dass immer nur eine physische Instanz eines Caches erstellt wird, sodass Sie niemals dieselben Daten zweimal abrufen müssen.Nichts davon soll heißen, dass jedes einzelne Softwareteil auf der Welt nach diesen strengen Maßstäben hoher Kohäsion und loser Kopplung entwickelt werden muss. Dies hängt von der Größe und dem Umfang des Projekts, Ihrem Team, Ihrem Budget, den Fristen usw. ab. Wenn Sie jedoch nach dem besten Design fragen (anstelle eines Singletons), dann ist dies das Richtige.
PS Wie andere bereits gesagt haben, ist es wahrscheinlich nicht die beste Idee für die abhängigen Klassen, sich dessen bewusst zu sein, dass sie einen Cache verwenden - das ist ein Implementierungsdetail, das sie einfach nie interessieren sollten. Abgesehen davon würde die Gesamtarchitektur immer noch sehr ähnlich wie oben dargestellt aussehen. Sie würden die einzelnen Schnittstellen nur nicht als Caches bezeichnen . Stattdessen würden Sie sie Services oder ähnliches nennen.
quelle
In dem Fall, den Sie angeben, scheint die Verwendung eines Singleton nicht das Problem zu sein, sondern das Symptom eines Problems - ein größeres architektonisches Problem.
Warum fragen die Bildschirme das Cache-Objekt nach Daten ab? Das Caching sollte für den Client transparent sein. Es sollte eine geeignete Abstraktion zum Bereitstellen der Daten geben, und die Implementierung dieser Abstraktion könnte das Zwischenspeichern verwenden.
Das Problem ist wahrscheinlich, dass Abhängigkeiten zwischen Teilen des Systems nicht korrekt eingerichtet sind, und dies ist wahrscheinlich systembedingt.
Warum müssen die Bildschirme wissen, woher sie ihre Daten beziehen? Warum werden die Bildschirme nicht mit einem Objekt versehen, das ihre Datenanforderungen erfüllen kann (hinter denen ein Cache versteckt ist)? Oft ist die Verantwortung für die Erstellung von Bildschirmen nicht zentralisiert, und es gibt keinen klaren Grund, die Abhängigkeiten einzuschleusen.
Auch hier beschäftigen wir uns mit großen architektonischen und gestalterischen Problemen.
Es ist auch sehr wichtig zu verstehen, dass die Lebensdauer eines Objekts vollständig von der Art und Weise getrennt werden kann, wie das Objekt zur Verwendung gefunden wird.
Ein Cache muss während der gesamten Lebensdauer der Anwendung aktiv sein (um nützlich zu sein), damit die Lebensdauer des Objekts der eines Singleton entspricht.
Das Problem mit Singleton (zumindest die übliche Implementierung von Singleton als statische Klasse / Eigenschaft) ist, wie andere Klassen, die es verwenden, es finden.
Bei einer statischen Singleton-Implementierung besteht die Konvention darin, sie einfach dort einzusetzen, wo sie benötigt wird. Dadurch wird die Abhängigkeit jedoch vollständig ausgeblendet und die beiden Klassen werden eng miteinander verbunden.
Wenn wir der Klasse die Abhängigkeit zur Verfügung stellen , ist diese Abhängigkeit explizit und alle konsumierende Klasse, die Kenntnis haben muss, ist der Vertrag, über den sie verfügen kann.
quelle
Ich habe gerade zu dieser Frage ein ganzes Kapitel geschrieben . Meist im Zusammenhang mit Spielen, aber das meiste sollte außerhalb von Spielen gelten.
tl; dr:
Das Singleton-Muster "Vierergruppen" hat zwei Funktionen: Sie können bequem von überall auf ein Objekt zugreifen und sicherstellen, dass nur eine Instanz davon erstellt werden kann. In 99% der Fälle kümmert es Sie nur um die erste Hälfte, und wenn Sie in der zweiten Hälfte karren, um sie zu erhalten, ist dies eine unnötige Einschränkung.
Darüber hinaus gibt es bessere Lösungen für den bequemen Zugriff. Ein Objekt global zu machen, ist die nukleare Option, um das zu lösen, und macht es einfach, Ihre Einkapselung zu zerstören. Alles, was an Globalen schlecht ist, gilt nur für Singletons.
Wenn Sie es nur verwenden, weil Sie viele Stellen im Code haben, die dasselbe Objekt berühren müssen, versuchen Sie, eine bessere Möglichkeit zu finden, es nur diesen Objekten zu geben, ohne es der gesamten Codebasis auszusetzen. Andere Lösungen:
Lassen Sie es ganz fallen. Ich habe viele Singleton-Klassen gesehen, die keinen Status haben und nur eine Fülle von Hilfsfunktionen sind. Diese brauchen überhaupt keine Instanz. Machen Sie sie einfach zu statischen Funktionen oder verschieben Sie sie in eine der Klassen, die die Funktion als Argument verwendet. Sie würden keine spezielle
Math
Klasse brauchen, wenn Sie nur tun könnten123.Abs()
.Gib es weiter. Die einfache Lösung, wenn eine Methode ein anderes Objekt benötigt, besteht darin, es einfach weiterzugeben. Es ist nichts Falsches daran, einige Objekte weiterzugeben.
Legen Sie es in die Basisklasse. Wenn Sie viele Klassen haben, die alle Zugriff auf ein bestimmtes Objekt benötigen und eine Basisklasse gemeinsam haben, können Sie dieses Objekt zu einem Mitglied in der Basis machen. Wenn Sie es konstruieren, übergeben Sie das Objekt. Jetzt können die abgeleiteten Objekte es alle bekommen, wenn sie es brauchen. Wenn Sie den Schutz aktivieren, stellen Sie sicher, dass das Objekt weiterhin gekapselt bleibt.
quelle
Es ist nicht der globale Zustand an sich, der das Problem ist.
Du musst dir wirklich nur Sorgen machen
global mutable state
. Der konstante Zustand wird nicht von Nebenwirkungen beeinflusst und ist daher weniger problematisch.Das Hauptanliegen bei Singleton ist, dass es die Kopplung erhöht und so das Testen erschwert. Sie können die Kopplung verringern, indem Sie den Singleton von einer anderen Quelle (z. B. einer Factory) beziehen. Auf diese Weise können Sie den Code von einer bestimmten Instanz entkoppeln (obwohl Sie stärker an die Factory gekoppelt sind (aber zumindest kann die Factory alternative Implementierungen für verschiedene Phasen haben)).
In Ihrer Situation, denke ich, können Sie damit durchkommen, solange Ihr Singleton tatsächlich eine Schnittstelle implementiert (so dass eine Alternative in anderen Situationen verwendet werden kann).
Ein weiterer großer Nachteil von Singletons ist, dass das Entfernen von Singletons aus dem Code und das Ersetzen durch etwas anderes zu einer sehr schwierigen Aufgabe wird (da ist wieder diese Kopplung).
quelle
le weekend
hoppla, das gehört zu unseren). Danke :-)Dann was? Da hat es keiner gesagt: Toolbox . Das ist, wenn Sie globale Variablen möchten .
Aber rate mal was? Es ist ein Singleton!
Und was ist ein Singleton?
Vielleicht beginnt hier die Verwirrung.
Für mich ist der Singleton ein Objekt, das immer und nur eine Instanz haben muss. Sie können jederzeit und überall darauf zugreifen, ohne sie instanziieren zu müssen. Deshalb ist es so eng mit
static
. Zum Vergleichstatic
ist es im Grunde dasselbe, außer dass es keine Instanz ist. Wir müssen es nicht instanziieren, wir können es auch nicht, weil es automatisch zugewiesen wird. Und das kann und bringt Probleme.Aus meiner Erfahrung heraus löste das einfache Ersetzen
static
von Singleton viele Probleme in einem mittelgroßen Projekt für Patchwork-Taschen, an dem ich beteiligt bin. Das bedeutet nur, dass es für schlecht gestaltete Projekte eine gewisse Verwendung hat. Ich denke , es ist zu viel Diskussion , wenn die Singletonmuster ist nützlich oder nicht , und ich kann nicht wirklich behaupten , wenn es in der Tat ist schlecht . Dennoch gibt es im Allgemeinen gute Argumente für Singleton gegenüber statischen Methoden .Ich bin mir nur sicher, dass Singletons schlecht sind, wenn wir sie verwenden, während wir gute Praktiken ignorieren. Damit ist es in der Tat nicht so einfach umzugehen. Aber schlechte Praktiken können auf jedes Muster angewendet werden. Und, ich weiß, es ist zu allgemein, um zu sagen, dass ... ich meine, es steckt einfach zu viel dahinter.
Versteh mich nicht falsch!
Einfach ausgedrückt, wie globaler Vars , Singletons sollte nach wie vor jederzeit vermieden werden . Vor allem, weil sie übermäßig missbraucht werden. Globale Unterschiede lassen sich jedoch nicht immer vermeiden, und wir sollten sie in diesem letzten Fall verwenden.
Wie auch immer, es gibt viele andere Vorschläge als die Toolbox, und genau wie die Toolbox hat auch jeder seine Anwendung ...
Andere Alternativen
Der beste Artikel, den ich gerade über Singletons gelesen habe, schlägt Service Locator als Alternative vor. Für mich ist das im Grunde genommen eine " statische Toolbox ", wenn man so will. Mit anderen Worten, machen Sie den Service Locator zu einem Singleton und Sie haben eine Toolbox. Das widerspricht natürlich dem ursprünglichen Vorschlag, den Singleton zu meiden, aber nur, um Singleton durchzusetzen, geht es darum, wie er verwendet wird, und nicht um das Muster an sich.
Andere schlagen Factory Pattern als Alternative vor. Es war die erste Alternative, die ich von einem Kollegen hörte, und wir haben sie schnell für unsere Verwendung als globale Variable entfernt . Es hat sicher seine Verwendung, aber auch Singletons.
Beide oben genannten Alternativen sind gute Alternativen. Aber alles hängt von Ihrer Nutzung ab.
Es ist einfach falsch, anzunehmen, dass Singletons um jeden Preis vermieden werden sollten ...
Die Unfähigkeiten (zu abstrakt oder Unterklasse) sind zwar da, aber na und? Es ist nicht dafür gedacht. Soweit ich das beurteilen kann, gibt es keine Unfähigkeit, eine Schnittstelle herzustellen . Es kann auch eine hohe Kopplung geben, aber das liegt nur daran, wie sie üblicherweise verwendet wird. Muss es nicht . Tatsächlich hat die Kopplung an sich nichts mit dem Singleton-Muster zu tun. Da dies geklärt ist, entfällt bereits die Schwierigkeit zu testen. Was die Schwierigkeit der Parallelisierung betrifft, so hängt dies von der Sprache und der Plattform ab, sodass dies wiederum kein Problem für das Muster darstellt.
Praxisbeispiele
Ich sehe oft 2, sowohl zugunsten als auch gegen Singletons. Webcache (mein Fall) und Protokolldienst .
Einige werden argumentieren , dass die Protokollierung ein perfektes Singleton-Beispiel ist, denn ich zitiere:
Während andere argumentieren, ist es schwierig, den Protokolldienst zu erweitern, sobald Sie feststellen, dass es sich eigentlich nicht nur um eine Instanz handeln sollte.
Nun, ich sage beide Argumente sind gültig. Das Problem liegt auch hier nicht im Singleton-Muster. Es ist auf architektonische Entscheidungen und Gewichtung, wenn Refactoring ein tragfähiges Risiko ist. Ein weiteres Problem, wenn Refactoring in der Regel die letzte erforderliche Korrekturmaßnahme ist.
quelle
java.awt.Toolkit
. Mein Standpunkt ist jedoch derselbe:Toolbox
Klingt eher nach einem Grabbag aus nicht zusammenhängenden Teilen und Stücken als nach einer zusammenhängenden Klasse mit einem einzigen Zweck. Klingt für mich nicht nach gutem Design. (Beachten Sie, dass der Artikel, auf den Sie sich beziehen, aus dem Jahr 2001 stammt, bevor Abhängigkeitsinjektion und DI-Container an der Tagesordnung waren.)Mein Hauptproblem mit dem Singleton-Entwurfsmuster ist, dass es sehr schwierig ist, gute Komponententests für Ihre Anwendung zu schreiben.
Jede Komponente, die von diesem "Manager" abhängig ist, fragt dazu ihre Singleton-Instanz ab. Und wenn Sie einen Komponententest für eine solche Komponente schreiben möchten, müssen Sie Daten in diese Singleton-Instanz einfügen, was möglicherweise nicht einfach ist.
Wenn andererseits Ihr "Manager" über einen Konstruktorparameter in die abhängigen Komponenten injiziert wird und die Komponente den konkreten Typ des Managers nicht kennt, nur eine Schnittstelle oder eine abstrakte Basisklasse, die der Manager implementiert, dann eine Einheit test kann beim Testen von Abhängigkeiten alternative Implementierungen des Managers bereitstellen.
Wenn Sie IOC-Container verwenden, um die Komponenten zu konfigurieren und zu instanziieren, aus denen Ihre Anwendung besteht, können Sie Ihren IOC-Container auf einfache Weise so konfigurieren, dass nur eine Instanz des "Managers" erstellt wird, sodass Sie dieselbe Instanz erhalten, die den globalen Anwendungscache steuert .
Wenn Sie sich jedoch nicht für Komponententests interessieren, kann ein Singleton-Entwurfsmuster durchaus in Ordnung sein. (aber ich würde es trotzdem nicht tun)
quelle
Ein Singleton ist nicht grundsätzlich schlecht , in dem Sinne, dass Design-Computing gut oder schlecht sein kann. Es kann immer nur richtig sein (gibt die erwarteten Ergebnisse) oder nicht. Es kann auch nützlich sein oder nicht, wenn es den Code klarer oder effizienter macht.
Ein Fall, in dem Singletons nützlich sind, ist, wenn sie eine Entität darstellen, die wirklich einzigartig ist. In den meisten Umgebungen sind Datenbanken eindeutig, es gibt wirklich nur eine Datenbank. Das Herstellen einer Verbindung zu dieser Datenbank kann kompliziert sein, da hierfür spezielle Berechtigungen erforderlich sind oder mehrere Verbindungstypen durchlaufen werden müssen. Die Organisation dieser Verbindung zu einem Singleton ist wahrscheinlich nur aus diesem Grund sehr sinnvoll.
Sie müssen jedoch auch sicherstellen, dass der Singleton wirklich ein Singleton und keine globale Variable ist. Dies ist wichtig, wenn es sich bei der einzelnen, eindeutigen Datenbank tatsächlich um vier Datenbanken handelt, jeweils eine für Produktions-, Staging-, Entwicklungs- und Testvorrichtungen. Ein Database Singleton findet heraus, mit welcher dieser Datenbanken eine Verbindung hergestellt werden soll, greift nach der einzelnen Instanz für diese Datenbank, stellt bei Bedarf eine Verbindung her und sendet sie an den Aufrufer zurück.
Wenn ein Singleton nicht wirklich ein Singleton ist (dies ist der Fall, wenn sich die meisten Programmierer aufregen), gibt es keine Möglichkeit, eine korrekte Instanz zu injizieren.
Ein weiteres nützliches Merkmal eines gut gestalteten Singleton-Musters ist, dass es häufig nicht beobachtbar ist. Der Anrufer bittet um eine Verbindung. Der Dienst, der es bereitstellt, kann ein zusammengefasstes Objekt zurückgeben oder, wenn er einen Test ausführt, für jeden Aufrufer ein neues Objekt erstellen oder stattdessen ein Scheinobjekt bereitstellen.
quelle
Die Verwendung des Singleton-Musters, das tatsächliche Objekte darstellt, ist vollkommen akzeptabel. Ich schreibe für das iPhone und es gibt viele Singletons im Cocoa Touch-Framework. Die Anwendung selbst wird durch einen Singleton der Klasse dargestellt
UIApplication
. Es gibt nur eine Anwendung, die Sie sind, daher ist es angemessen, diese mit einem Singleton darzustellen.Die Verwendung eines Singletons als Datenmanager-Klasse ist in Ordnung, solange das Design stimmt. Wenn es sich um einen Bereich von Dateneigenschaften handelt, ist dies nicht besser als der globale Bereich. Wenn es eine Reihe von Gettern und Setzern ist, ist das besser, aber immer noch nicht großartig. Wenn es sich um eine Klasse handelt, die wirklich alle Schnittstellen zu Daten verwaltet, einschließlich des Abrufens entfernter Daten, des Zwischenspeicherns, Einrichtens und Herunterfahrens ... Das könnte sehr nützlich sein.
quelle
Singletons sind nur die Projektion einer serviceorientierten Architektur in ein Programm.
Eine API ist ein Beispiel für einen Singleton auf Protokollebene. Sie greifen auf Twitter, Google usw. über Singletons zu. Warum werden Singletons in einem Programm schlecht?
Es kommt darauf an, wie Sie sich ein Programm vorstellen. Wenn Sie sich ein Programm eher als eine Gesellschaft von Diensten vorstellen als zufällig gebundene zwischengespeicherte Instanzen, dann sind Singletons absolut sinnvoll.
Singletons sind ein Service Access Point. Die öffentliche Schnittstelle zu einer eng gebundenen Bibliothek von Funktionen, die möglicherweise eine sehr ausgefeilte interne Architektur verbirgt.
Ich sehe einen Singleton also nicht anders als eine Fabrik. Dem Singleton können Konstruktorparameter übergeben werden. Er kann in einem Kontext erstellt werden, der beispielsweise weiß, wie der Standarddrucker anhand aller möglichen Auswahlmechanismen aufgelöst wird. Zum Testen können Sie Ihr eigenes Mock einfügen. Es kann also sehr flexibel sein.
Der Schlüssel befindet sich intern in einem Programm, wenn ich es ausführe und ein bisschen Funktionalität benötige. Ich kann auf den Singleton mit der vollen Sicherheit zugreifen, dass der Dienst verfügbar und einsatzbereit ist. Dies ist der Schlüssel, wenn in einem Prozess verschiedene Threads beginnen, die eine Statusmaschine durchlaufen müssen, um als bereit angesehen zu werden.
Normalerweise würde ich eine
XxxService
Klasse umschließen, die ein Singleton um eine Klasse umschließtXxx
. Der Singleton ist überhaupt nicht in der KlasseXxx
, er ist in eine andere Klasse aufgeteiltXxxService
. Dies liegt daran,Xxx
dass mehrere Instanzen möglich sind, obwohl dies nicht wahrscheinlich ist. Wir möchten jedoch, dassXxx
auf jedem System eine Instanz global verfügbar ist.XxxService
bietet eine schöne Trennung von Bedenken.Xxx
Es muss keine Singleton-Richtlinie erzwungen werden, wir können sie jedoch bei BedarfXxx
als Singleton verwenden.So etwas wie:
quelle
Erste Frage, finden Sie viele Fehler in der Anwendung? Vielleicht vergessen, den Cache zu aktualisieren oder den Cache zu beschädigen oder es schwierig zu finden, ihn zu ändern? (Ich erinnere mich, dass eine App die Größe nicht ändern würde, wenn Sie nicht auch die Farbe ändern würden. Sie können jedoch die Farbe zurück ändern und die Größe beibehalten.)
Was Sie tun würden, ist, diese Klasse zu haben, aber ALLE STATISCHEN MITGLIEDER ZU ENTFERNEN. Ok, das ist nicht notwendig, aber ich empfehle es. In Wirklichkeit initialisieren Sie die Klasse einfach wie eine normale Klasse und übergeben den Zeiger. Sagen Sie nicht "ClassIWant.APtr (). LetMeChange.ANYTHINGATALL (). Andhave_no_structure ()".
Es ist mehr Arbeit, aber wirklich weniger verwirrend. Einige Orte, an denen Sie Dinge nicht ändern sollten, die Sie jetzt nicht mehr können, da sie nicht mehr global sind. Alle meine Manager-Klassen sind reguläre Klassen, behandeln Sie es einfach so.
quelle
IMO, dein Beispiel klingt okay. Ich würde vorschlagen, wie folgt auszurechnen: Cache-Objekt für jedes (und hinter jedem) Datenobjekt; Cache-Objekte und die DB-Accessor-Objekte haben dieselbe Schnittstelle. Dies gibt die Möglichkeit, Caches in und aus dem Code zu tauschen. Plus es gibt eine einfache Expansionsroute.
Grafik:
DB-Accessor und Cache können vom selben Objekt oder Ententyp erben, sodass sie unabhängig davon wie dasselbe Objekt aussehen. Solange Sie stecken / kompilieren / testen können und es noch funktioniert.
Dies entkoppelt Dinge, sodass Sie neue Caches hinzufügen können, ohne ein Uber-Cache-Objekt bearbeiten zu müssen. YMMV. IANAL. USW.
quelle
Ein bisschen spät zur Party, aber trotzdem.
Singleton ist wie alles andere ein Werkzeug in einer Toolbox. Hoffentlich haben Sie mehr in Ihrem Werkzeugkasten als nur einen einzigen Hammer.
Bedenken Sie:
vs
1. Fall führt zu hoher Kopplung usw .; 2. Weg hat keine Probleme @Aaronaught beschreibt, soweit ich das beurteilen kann. Es dreht sich alles darum, wie Sie es verwenden.
quelle
Lassen Sie jeden Bildschirm den Manager in seinem Konstruktor aufnehmen.
Wenn Sie Ihre App starten, erstellen Sie eine Instanz des Managers und geben sie weiter.
Dies wird als Inversion of Control bezeichnet und ermöglicht es Ihnen, den Controller bei Konfigurationsänderungen und in Tests auszutauschen. Darüber hinaus können Sie mehrere Instanzen Ihrer Anwendung oder Teile Ihrer Anwendung parallel ausführen (gut zum Testen!). Zuletzt stirbt Ihr Manager mit seinem eigenen Objekt (der Startklasse).
So strukturieren Sie Ihre App wie einen Baum, in dem die übergeordneten Elemente alle untergeordneten Elemente enthalten. Implementieren Sie keine App wie ein Mesh, bei der jeder jeden kennt und sich über globale Methoden findet.
quelle