Ich habe den Singleton-Artikel auf Wikipedia gelesen und bin auf dieses Beispiel gestoßen:
public class Singleton {
// Private constructor prevents instantiation from other classes
private Singleton() {}
/**
* SingletonHolder is loaded on the first execution of Singleton.getInstance()
* or the first access to SingletonHolder.INSTANCE, not before.
*/
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
Obwohl mir das Verhalten dieses Singleton sehr gut gefällt, kann ich nicht sehen, wie ich es anpassen kann, um Argumente in den Konstruktor aufzunehmen. Was ist der bevorzugte Weg, dies in Java zu tun? Würde ich so etwas tun müssen?
public class Singleton
{
private static Singleton singleton = null;
private final int x;
private Singleton(int x) {
this.x = x;
}
public synchronized static Singleton getInstance(int x) {
if(singleton == null) singleton = new Singleton(x);
return singleton;
}
}
Vielen Dank!
Bearbeiten: Ich glaube, ich habe mit meinem Wunsch, Singleton zu verwenden, einen Sturm der Kontroversen ausgelöst. Lassen Sie mich meine Motivation erklären und hoffentlich kann jemand eine bessere Idee vorschlagen. Ich verwende ein Grid-Computing-Framework, um Aufgaben parallel auszuführen. Im Allgemeinen habe ich so etwas:
// AbstractTask implements Serializable
public class Task extends AbstractTask
{
private final ReferenceToReallyBigObject object;
public Task(ReferenceToReallyBigObject object)
{
this.object = object;
}
public void run()
{
// Do some stuff with the object (which is immutable).
}
}
Obwohl ich lediglich einen Verweis auf meine Daten an alle Aufgaben weitergebe, werden die Daten bei der Serialisierung der Aufgaben immer wieder kopiert. Ich möchte das Objekt auf alle Aufgaben verteilen. Natürlich könnte ich die Klasse so ändern:
// AbstractTask implements Serializable
public class Task extends AbstractTask
{
private static ReferenceToReallyBigObject object = null;
private final String filePath;
public Task(String filePath)
{
this.filePath = filePath;
}
public void run()
{
synchronized(this)
{
if(object == null)
{
ObjectReader reader = new ObjectReader(filePath);
object = reader.read();
}
}
// Do some stuff with the object (which is immutable).
}
}
Wie Sie sehen können, habe ich auch hier das Problem, dass das Übergeben eines anderen Dateipfads nichts bedeutet, nachdem der erste übergeben wurde. Aus diesem Grund gefällt mir die Idee für ein Geschäft, das in den Antworten veröffentlicht wurde. Anstatt die Logik zum Laden der Datei in die Ausführungsmethode aufzunehmen, wollte ich diese Logik in eine Singleton-Klasse abstrahieren. Ich werde kein weiteres Beispiel nennen, aber ich hoffe, Sie haben die Idee. Bitte lassen Sie mich Ihre Ideen für einen eleganteren Weg hören, um das zu erreichen, was ich versuche. Danke nochmal!
Antworten:
Ich werde meinen Standpunkt sehr deutlich machen: Ein Singleton mit Parametern ist kein Singleton .
Ein Singleton ist per Definition ein Objekt, das Sie nur einmal instanziieren möchten. Wenn Sie versuchen, dem Konstruktor Parameter zuzuführen, wozu dient der Singleton?
Sie haben zwei Möglichkeiten. Wenn Sie möchten, dass Ihr Singleton mit einigen Daten initialisiert wird, können Sie ihn nach der Instanziierung wie folgt mit Daten laden :
Wenn die Operation, die Ihr Singleton ausführt, wiederkehrend ist und jedes Mal andere Parameter aufweist, können Sie die Parameter auch an die auszuführende Hauptmethode übergeben:
In jedem Fall ist die Instanziierung immer parameterlos. Andernfalls ist Ihr Singleton kein Singleton.
quelle
Ich denke, Sie brauchen so etwas wie eine Fabrik , um Objekte mit verschiedenen Parametern instanziieren und wiederverwenden zu lassen. Es könnte implementiert werden, indem ein synchronisierter
HashMap
oderConcurrentHashMap
ein Parameter (Integer
zum Beispiel ein) Ihrer parametrierbaren 'Singleton'-Klasse zugeordnet wird.Obwohl Sie möglicherweise an den Punkt gelangen, an dem Sie stattdessen reguläre Nicht-Singleton-Klassen verwenden sollten (z. B. 10.000 unterschiedlich parametrisierte Singleton-Klassen).
Hier ist ein Beispiel für ein solches Geschäft:
Um es noch weiter
enum
voranzutreiben, können die Java s auch als parametrisierte Singletons betrachtet (oder als solche verwendet) werden, obwohl nur statische Varianten mit einer festen Anzahl zulässig sind.Wenn Sie jedoch eine verteilte 1- Lösung benötigen, sollten Sie eine laterale Caching-Lösung in Betracht ziehen. Zum Beispiel: EHCache, Terrakotta usw.
1 im Sinne einer Spannweite mehrerer VMs auf wahrscheinlich mehreren Computern.
quelle
Sie können eine konfigurierbare Initialisierungsmethode hinzufügen, um die Instanziierung vom Abrufen zu trennen.
Dann können Sie
Singleton.init(123)
einmal anrufen , um es zu konfigurieren, zum Beispiel beim Start Ihrer App.quelle
Sie können auch das Builder-Muster verwenden, wenn Sie zeigen möchten, dass einige Parameter obligatorisch sind.
Dann können Sie es wie folgt erstellen / instanziieren / parametrisieren :
quelle
Die Anweisung " Ein Singleton mit Parametern ist kein Singleton " ist nicht vollständig korrekt . Wir müssen dies eher aus der Anwendungsperspektive als aus der Codeperspektive analysieren.
Wir erstellen eine Singleton-Klasse, um eine einzelne Instanz eines Objekts in einem Anwendungslauf zu erstellen. Mit einem Konstruktor mit Parametern können Sie Ihrem Code Flexibilität hinzufügen, um einige Attribute Ihres Singleton-Objekts bei jeder Ausführung Ihrer Anwendung zu ändern. Dies ist keine Verletzung des Singleton-Musters. Es sieht nach einer Verletzung aus, wenn Sie dies aus Code-Sicht sehen.
Design Patterns sollen uns helfen, flexiblen und erweiterbaren Code zu schreiben, und uns nicht daran hindern, guten Code zu schreiben.
quelle
Verwenden Sie Getter und Setter, um die Variable festzulegen und den Standardkonstruktor privat zu machen. Dann benutze:
quelle
Überrascht, dass niemand erwähnt hat, wie ein Logger erstellt / abgerufen wird. Im Folgenden wird beispielsweise gezeigt, wie der Log4J-Logger abgerufen wird.
Es gibt einige Ebenen von Indirektionen, aber der Schlüsselteil befindet sich unter der Methode, die so ziemlich alles darüber erzählt, wie es funktioniert. Es verwendet eine Hash-Tabelle, um die vorhandenen Logger zu speichern, und der Schlüssel wird vom Namen abgeleitet. Wenn der Logger für einen Vornamen nicht vorhanden ist, verwendet er eine Factory, um den Logger zu erstellen, und fügt ihn dann der Hash-Tabelle hinzu.
quelle
Änderung des Singleton-Musters unter Verwendung der Red Pugh-Initialisierung bei Bedarf . Dies ist threadsicher ohne den Aufwand spezialisierter Sprachkonstrukte (dh flüchtig oder synchronisiert):
quelle
finally { RInterfaceHL.rloopHandler = null; }
ingetInstance
, weil die statischen Referenz einen Speicherverlust verursachen kann , wenn wir nicht aufpassen. In Ihrem Fall sieht es so aus, als wäre es kein Problem, aber ich könnte mir ein Szenario vorstellen, in dem das übergebene Objekt groß ist und nur vonRInterfaceHL
ctor verwendet wird , um einige Werte abzurufen und keinen Verweis darauf zu behalten.return SingletonHolder.INSTANCE
würde genauso gut funktionieren ingetInstance
. Ich glaube nicht, dass hier eine Kapselung erforderlich ist, da die äußere Klasse die Innereien der inneren Klasse bereits kennt, sie sind eng miteinander verbunden: Sie weiß, dass sierloopHandler
vor dem Anruf init benötigt. Auch der private Konstruktor hat keine Auswirkung, da das private Material der inneren Klasse einfach der äußeren Klasse zur Verfügung steht.Der Grund, warum Sie nicht verstehen können, wie Sie das erreichen, was Sie versuchen, ist wahrscheinlich, dass das, was Sie versuchen, keinen wirklichen Sinn ergibt. Sie möchten
getInstance(x)
mit unterschiedlichen Argumenten aufrufen , aber immer das gleiche Objekt zurückgeben? Welches Verhalten möchten Sie, wenn Sie anrufengetInstance(2)
und danngetInstance(5)
?Wenn Sie möchten, dass dasselbe Objekt unterschiedlich ist, der interne Wert jedoch unterschiedlich ist. Nur so ist es immer noch ein Singleton. Dann müssen Sie sich überhaupt nicht um den Konstruktor kümmern. Sie setzen den Wert einfach
getInstance()
auf dem Weg des Objekts ein. Natürlich verstehen Sie, dass alle anderen Verweise auf den Singleton jetzt einen anderen internen Wert haben.Wenn Sie möchten ,
getInstance(2)
undgetInstance(5)
verschiedene Objekte zurückzukehren, auf der anderen Seite, die Sie verwenden nicht die Singleton - Muster, sind Sie die Fabrik Muster.quelle
In Ihrem Beispiel verwenden Sie keinen Singleton. Beachten Sie Folgendes: Wenn Sie Folgendes tun (vorausgesetzt, Singleton.getInstance war tatsächlich statisch):
Dann ist der Wert von obj2.x 3, nicht 4. Wenn Sie dies tun müssen, machen Sie es zu einer einfachen Klasse. Wenn die Anzahl der Werte klein und fest ist, können Sie eine verwenden
enum
. Wenn Sie Probleme mit einer übermäßigen Objektgenerierung haben (was normalerweise nicht der Fall ist), können Sie Caching-Werte in Betracht ziehen (und Quellen überprüfen oder Hilfe dazu erhalten, da es offensichtlich ist, wie Caches ohne die Gefahr von Speicherlecks erstellt werden).Vielleicht möchten Sie diesen Artikel auch lesen, da Singletons sehr leicht überbeansprucht werden können.
quelle
Ein weiterer Grund, warum Singletons ein Anti-Pattern sind, besteht darin, dass sie, wenn sie gemäß den Empfehlungen mit einem privaten Konstruktor geschrieben wurden, sehr schwer zu klassifizieren und für die Verwendung in bestimmten Komponententests zu konfigurieren sind. Wäre zum Beispiel für die Pflege von Legacy-Code erforderlich.
quelle
Wenn Sie eine Singleton-Klasse erstellen möchten, die als Kontext dient, sollten Sie eine Konfigurationsdatei haben und die Parameter aus der Datei in instance () lesen.
Wenn die Parameter, die die Singleton-Klasse speisen, während der Ausführung Ihres Programms dynamisch abgerufen werden, verwenden Sie einfach eine statische HashMap, in der verschiedene Instanzen in Ihrer Singleton-Klasse gespeichert sind, um sicherzustellen, dass für jeden Parameter nur eine Instanz erstellt wird.
quelle
Dies ist kein Einzelfall, kann aber möglicherweise Ihr Problem beheben.
quelle
Wenn wir das Problem als "wie man Singleton mit state macht" betrachten, ist es nicht notwendig, den state als Konstruktorparameter zu übergeben. Ich bin mit den Posts einverstanden, die die Zustände initialisieren oder die set-Methode verwenden, nachdem die Singleton-Instanz abgerufen wurde.
Eine andere Frage ist: Ist es gut, Singleton mit Staat zu haben?
quelle
Könnten wir so etwas nicht machen:
quelle
Trotz allem, was einige behaupten mögen, ist hier ein Singleton mit Parametern im Konstruktor
Das Singleton-Muster sagt:
die mit diesem Beispiel respektiert werden.
Warum nicht direkt die Eigenschaft einstellen? Es ist ein Lehrbuchfall, um zu zeigen, wie wir einen Singleton mit einem Konstruktor mit Parameter erhalten können, aber es kann in einigen Situationen nützlich sein. Zum Beispiel in Vererbungsfällen, um den Singleton zu zwingen, einige Eigenschaften der Oberklasse festzulegen.
quelle
Ich habe Angst, dies als Antwort zu posten, aber ich verstehe nicht, warum niemand darüber nachdenkt. Vielleicht wurde diese Antwort auch schon gegeben, ich habe sie einfach nicht verstanden.
Da die
getInstance()
Instanz jedes Mal dieselbe Instanz zurückgibt, denke ich, dass dies funktionieren könnte. Wenn dies zu viel falsch ist, werde ich es löschen, ich bin nur an diesem Thema interessiert.quelle
Ich denke, das ist ein häufiges Problem. Das Trennen der "Initialisierung" des Singletons vom "Get" des Singletons funktioniert möglicherweise (in diesem Beispiel wird eine Variante der doppelt aktivierten Sperre verwendet).
quelle
Singleton ist natürlich ein "Anti-Pattern" (unter der Annahme einer Definition einer Statik mit variablem Zustand).
Wenn Sie einen festen Satz unveränderlicher Wertobjekte wünschen, sind Aufzählungen der richtige Weg. Für einen großen, möglicherweise offenen Satz von Werten können Sie ein Repository in irgendeiner Form verwenden - normalerweise basierend auf einer
Map
Implementierung. Wenn Sie sich mit Statik beschäftigen, sollten Sie natürlich vorsichtig mit dem Threading umgehen (entweder ausreichend breit synchronisieren oderConcurrentMap
entweder überprüfen, ob ein anderer Thread Sie nicht geschlagen hat, oder irgendeine Form von Futures verwenden).quelle
Singletons gelten allgemein als Anti-Patterns und sollten nicht verwendet werden. Sie machen es nicht einfach, Code zu testen.
Ein Singleton mit einem Argument macht sowieso keinen Sinn - was würde passieren, wenn Sie schreiben würden:
Ihr Singleton ist auch nicht threadsicher, da mehrere Threads gleichzeitig aufrufen können,
getInstance
was dazu führt, dass mehr als eine Instanz erstellt wird (möglicherweise mit unterschiedlichen Werten vonx
).quelle