Was ist eine Alternative zu Singleton?

114

Wir haben eine Klasse, die Konfigurationsinformationen für die Anwendung enthält. Früher war es ein Singleton. Nach einigen architektonischen Überprüfungen wurde uns gesagt, wir sollten den Singleton entfernen. Wir haben einige Vorteile gesehen, wenn Singleton beim Komponententest nicht verwendet wurde, da wir verschiedene Konfigurationen gleichzeitig testen können.

Ohne Singleton müssen wir die Instanz überall in unserem Code weitergeben. Es wird so chaotisch, dass wir einen Singleton-Wrapper geschrieben haben. Jetzt portieren wir denselben Code nach PHP und .NET. Ich frage mich, ob es ein besseres Muster gibt, das wir für das Konfigurationsobjekt verwenden können.

John Mill
quelle

Antworten:

130

Der Google Testing-Blog enthält eine Reihe von Einträgen zum Vermeiden von Singleton (um testbaren Code zu erstellen). Vielleicht kann Ihnen das helfen:

Im letzten Artikel wird ausführlich erläutert, wie Sie die Erstellung neuer Objekte in eine Factory verschieben, um die Verwendung von Singletons zu vermeiden. Auf jeden Fall lesenswert.

Kurz gesagt, wir verlegen alle neuen Betreiber in eine Fabrik. Wir gruppieren alle Objekte mit ähnlicher Lebensdauer in einer einzigen Fabrik.

FrankS
quelle
3
*** Verwenden der Abhängigkeitsinjektion zur Vermeidung von Singletons
Justin
Diese Artikel sind so gut wie die Google C ++ - Programmierstandards!
2
Nicht wirklich. Der Ratschlag „Verwenden Sie keine statischen Methoden“ verstößt beispielsweise direkt gegen das Minimalschnittstellenprinzip von Scott Meyers / Herb Sutters. Es gibt nützliche Ratschläge, aber es fehlt ihnen der Beitrag mehrerer Köpfe.
Matthieu M.
@FrankS warum hast du die Reihenfolge der Links geändert? Anfangs war es in guter chronologischer Reihenfolge.
Cregox
@Cawas eigentlich keine Ahnung, das war vor mehr als vier Jahren, also hatte ich wohl damals einige Gründe dafür :-)
FrankS
15

Am besten verwenden Sie stattdessen ein Factory-Muster. Wenn Sie eine neue Instanz Ihrer Klasse (in der Factory) erstellen, können Sie die 'globalen' Daten in das neu erstellte Objekt einfügen, entweder als Referenz auf eine einzelne Instanz (die Sie in der Factory-Klasse speichern) oder indem Sie die entsprechenden kopieren Daten in das neue Objekt.

Alle Ihre Objekte enthalten dann die Daten, die früher im Singleton lebten. Ich glaube nicht, dass es insgesamt einen großen Unterschied gibt, aber es kann das Lesen Ihres Codes erleichtern.

gbjbaanb
quelle
1
Ich stimme der Aussage "Best Way" nicht zu, aber +1 für eine gute Alternative.
Tylermac
Das Problem bei diesem Ansatz besteht darin, dass jedes neue Objekt (oder Verweise) enthält, was möglicherweise ein riesiger Datenklumpen sein kann. var_dump () für eines dieser Gob-haltigen Objekte führt sehr schnell zu riesigen Auflistungen, die frei mit Rekursionswarnungen gespickt sind . Es ist verdammt hässlich, kann nicht besonders effizient sein und lässt die Dinge verdreht erscheinen. Ich persönlich habe jedoch keinen besseren Weg gefunden. Ich habe die "factory" -Methode dahingehend gebogen, dass __construct () verwendet wird, um auf eine globale zu verweisen. Es fühlt sich jedoch so an, als wäre alles nach hinten gebeugt, um dem gefürchteten Singleton
auszuweichen
2
@ EastGhostCom: Wir könnten genauso gut den Singleton verwenden und aufhören zu versuchen, uns die Dinge schwer zu machen :)
gbjbaanb
5

Ich sage hier vielleicht das Offensichtliche, aber gibt es einen Grund, warum Sie kein Abhängigkeitsinjektions-Framework wie z Spring oder Guice verwenden können ? (Ich glaube, Spring ist jetzt auch für .NET verfügbar).

Auf diese Weise kann das Framework eine einzelne Kopie der Konfigurationsobjekte enthalten, und Ihre Beans (Dienste, DAOs usw.) müssen sich nicht darum kümmern, sie nachzuschlagen.

Dies ist der Ansatz, den ich normalerweise verfolge!


quelle
4

Wenn Sie Spring Framework verwenden , können Sie einfach eine reguläre Bean erstellen. Standardmäßig (oder wenn Sie dies explizit festlegen scope="singleton") wird nur eine Instanz der Bean erstellt und diese Instanz wird jedes Mal zurückgegeben, wenn die Bean in einer Abhängigkeit verwendet oder über abgerufen wirdgetBean() .

Sie erhalten den Vorteil der einzelnen Instanz ohne die Kopplung des Singleton-Musters.

Gene Gotimer
quelle
3
Oh die Ironie - verwenden Sie (Singleton) Spring Beans, um Ihren Singleton zu ersetzen ...
Zack Macomber
4

Die Alternative besteht darin, das zu übergeben, was Sie benötigen, anstatt ein Objekt nach Dingen zu fragen.

koen
quelle
4

Sammeln Sie keine Verantwortlichkeiten für ein einzelnes Konfigurationsobjekt da dies zu einem sehr großen Objekt führt, das sowohl schwer zu verstehen als auch fragil ist.

Wenn Sie beispielsweise einen anderen Parameter für eine bestimmte Klasse benötigen, ändern Sie das ConfigurationObjekt und kompilieren alle Klassen, die es verwenden, neu. Das ist etwas problematisch.

Versuchen Sie, Ihren Code umzugestalten, um ein gemeinsames, globales und großes ConfigurationObjekt zu vermeiden . Übergeben Sie nur die erforderlichen Parameter an Clientklassen:

class Server {

    int port;

    Server(Configuration config) {
        this.port = config.getServerPort();
    } 

}

sollte überarbeitet werden, um:

 class Server {

    public Server(int port) {
       this.port = port;
    }
 }

Ein Abhängigkeitsinjektions-Framework wird hier viel helfen, ist aber nicht unbedingt erforderlich.

dfa
quelle
Ja, das ist wirklich ein guter Punkt. Ich habe das schon mal gemacht. Mein großes Konfigurationsobjekt implementierte Schnittstellen wie MailServiceConf, ServerConf .. als das Dependency Injection Framework die Konfiguration an Klassen übergibt, sodass meine Klassen nicht von einem großen Konfigurationsobjekt abhängig waren.
Caltuntas
1

Sie können das gleiche Verhalten von Singleton mithilfe statischer Methoden erzielen. Steve Yegge erklärt es sehr gut in diesem Beitrag.

Youssef
quelle
Eigentlich ist der Artikel ziemlich gut und es heißt nicht, dass Sie stattdessen statische Methoden verwenden sollten. Stattdessen gibt er an, dass statische Methoden ebenfalls nur Singletons sind, und empfiehlt am Ende die Verwendung des Factory-Methodenmusters: "Abschließend möchte ich sagen, dass Sie stattdessen das Factory-Methodenmuster verwenden sollten, wenn Sie immer noch Singleton-Objekte verwenden müssen. ... "
FrankS
0

Ist eine Klasse möglich, die nur statische Methoden und Felder enthält? Ich bin mir nicht sicher, wie genau Ihre Situation ist, aber es könnte sich lohnen, sie zu untersuchen.

Thomas Owens
quelle
1
Wenn die Klasse zustandslos ist, sollte es sich um eine statische Klasse handeln.
AlbertoPL
1
Es ist in C ++ - das Muster ist als Monostate bekannt.
0

Hängt davon ab, welche Werkzeuge / Frameworks usw. verwendet werden. Mit Tools für Abhängigkeitsinjektion / ioc kann man häufig immer noch Singleton-Leistung / -Optimierungen erzielen, indem der di / ioc-Container das Singleton-Verhalten für die erforderliche Klasse verwendet (z. B. eine IConfigSettings-Schnittstelle), indem immer nur eine Instanz der Klasse erstellt wird. Dies könnte zum Testen noch ersetzt werden

Alternativ kann eine Factory verwendet werden, um die Klasse zu erstellen und jedes Mal dieselbe Instanz zurückzugeben, wenn Sie sie anfordern. Zum Testen kann jedoch eine stubbed / verspottete Version zurückgegeben werden

Saret
quelle
0

Überprüfen Sie die Möglichkeit, die Konfiguration als Rückrufschnittstelle vorzunehmen. Ihr konfigurationssensitiver Code sieht also folgendermaßen aus:

MyReuseCode.Configure(IConfiguration)

Der System-Init-Code sieht folgendermaßen aus:

Library.init(MyIConfigurationImpl)
Dewfy
quelle
0

Sie können ein Abhängigkeitsinjektionsframework verwenden, um das Übergeben des Konfigurationsobjekts zu erleichtern. Ein anständiges ist ninject, das den Vorteil hat, Code anstelle von XML zu verwenden.

Colin Gravill
quelle
0

Vielleicht auch nicht sehr sauber, aber Sie könnten vielleicht die Informationsbits, die Sie geändert haben möchten, an die Methode übergeben, die den Singleton erstellt - anstatt zu verwenden

public static Singleton getInstance() {
    if(singleton != null)
        createSingleton();
        return singleton;
    }
}

Sie können createSingleton(Information info)direkt beim Start der Anwendung aufrufen (und in den setUp-Methoden der Unit-Tests).

Roland Ewald
quelle
-1

Singletons sind nicht böse, aber das Designmuster ist fehlerhaft. Ich habe eine Klasse, die ich zur Laufzeit nur eine einzige Instanz davon erstellen möchte, während des Komponententests jedoch mehrere isolierte Instanzen erstellen möchte, um deterministische Ergebnisse sicherzustellen.

DI mit Spring usw. ist eine sehr gute Option, aber nicht die einzige.

Codematrix
quelle