PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Problem mit singleton unter java


lola
2004-11-28, 18:52:19
Hi

ich hab eine Singleton Klasse mit Namen Data erzeugt und möchte nun verhindern daß eine 2. Instanz erzeugt werden kann wenn schon eine erzeugt wurde.Kann mir da einer weiterhelfen wie ich das realisieren könnte? Hier der Code:

final class Data {
private String name;
// create the one and only object “in store”
private static Data theData = new Data ("NoName");

// a private constructor
private Data (String n) {this.name = n;}
// a reference to the object
public static Data createData() {
return theData;
}
// other methods of class Data
public void setName (String n) { name = n; }
public String getName () { return name;
}
}

HellHorse
2004-11-28, 21:34:14
Tuts du ja schon. Der Konstrukor ist private, also wird verhindert, dass er von ausserhalb der Klasse aufgeraufen wird.
Ist im Moment nicht lazy aber das ist ja nicht Voraussetzung für ein singelton.

BTW, [ code ] Tags regeln.

lola
2004-11-28, 21:54:12
Nee,ich meinte ich kann ja trotzdem 2 instanzen erzeugen,zB:


public static void main(String[] args) {

Data firstData = Data.createData();
System.out.println(firstData.getName());
firstData.setName("one");
System.out.println(firstData.getName());
//firstData.Ausgabe();
//firstData.Eingabe();
//firstData.Ausgabe();
Data secondData = Data.createData();
System.out.println(secondData.getName());
secondData.setName("two");
System.out.println(secondData.getName());
System.out.println(firstData.getName());
}



Wie könnte ich verhindern dass da die 2. Instanz erzeugt wird (also secondData)?

HellHorse
2004-11-28, 22:14:25
Sorry, aber genau dieser Code beweist, das du eben nicht neue Instanzen kreierst. Wie denn auch, createData verwendet ja auch nicht new sondern gibt bloss eine Klassenvariable, die zufälligerweise die singleton Instanz ist zurück.

Falls du mir immer noch nicht glaubst:

System.out.println("Are firstData and secondData the very same object? " + (firstData == secondData ? "Yes" : "No" ));


update:
Es geht eigentlich schon, aber nicht mit deinem Code und ich glaube nicht, dass du das meintest. Wenn du das verhindern willst, musst du entweder einen SecurityManager verwenden, der das verbietet oder im Konstrukor einen check machen, ob die singleton Instanz schon kreiert wurde. Falls ja wirfst du eine Exception.

public class NotSoSingleton {

public static void main(String[] args) {
try {
Class<Data> dataClass = Data.class;
Constructor<Data> dataConstructor = dataClass.getDeclaredConstructor(String.class);
dataConstructor.setAccessible(true);
Data data1 = dataConstructor.newInstance("one");
Data data2 = dataConstructor.newInstance("two");
System.out.println(data1 != data2 ? "OK" : "NOK");
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}

lola
2004-11-29, 17:44:14
update:
Es geht eigentlich schon, aber nicht mit deinem Code und ich glaube nicht, dass du das meintest. Wenn du das verhindern willst, musst du entweder einen SecurityManager verwenden, der das verbietet oder im Konstrukor einen check machen, ob die singleton Instanz schon kreiert wurde. Falls ja wirfst du eine Exception.



Genau das habe ich gemeint,danke für die Hilfe! :smile:

HellHorse
2004-11-30, 10:53:10
Update2:
Das mit dem check im Konstruktor lässt sich auf die gleiche Weise aushebeln. Du musst also zwangläufig einen SecurityManager verwenden.

grakaman
2004-11-30, 11:22:08
Den Check auf null macht man doch in der Factory-Methode.


class myclass
{
private static myclass = null;

private myclass()
{

}

public static myclass CreateInstance()
{
if(myclass == null)
myclass = new myclass();

return myclass;
}
}


Wenn du die Instanzierung in der Factory vornimmst, hast du den Vorteil über die Factory z.B. einen überladenen Konstruktor aufzurufen. Allerdings ist das Bsp. nicht Threadsafe. Wie das in Java Threadsafe implementiert wird, weiß ich nicht.

HellHorse
2004-11-30, 12:14:58
Den Check auf null macht man doch in der Factory-Methode.
Wenn du es lazy willst, dann schon.

update:
Der check im Konstruktor war nur für den Fall gedacht, dass sich jemand den private Konstruktor über Reflection schnappt, als accessible marktiert und dann aufruft. Wie schon gesagt, auch dieser Test lässt sich aushebeln.

Wenn du die Instanzierung in der Factory vornimmst, hast du den Vorteil über die Factory z.B. einen überladenen Konstruktor aufzurufen.
Kannst du sonst auch machen.

Allerdings ist das Bsp. nicht Threadsafe. Wie das in Java Threadsafe implementiert wird, weiß ich nicht.
synchronzied und gut ist.

grakaman
2004-11-30, 12:35:41
Wenn du es lazy willst, dann schon.


:confused:


Kannst du sonst auch machen.


Und wie?

HellHorse
2004-11-30, 14:03:00
:confused:
Normalerweise (so wie du es gepostet hast, und nicht so wie lola es gepostet hat) ist ein singleton lazy initialized. Will heissen die singleton Instanz wird erst kreiert, wenn das erste mal jemand die factory Methode aufruft und nicht schon wenn die Klasse geladen wird, daher ist auch der check nötig. Das ist aber nicht Anforderung an ein singleton.

Und wie?
Hoffe wir reden vom gleichen:

final class Data {
private String name;

// create the one and only object \u201cin store\u201d
private static Data theData = new Data("NoName");

// a private default constructor
private Data() {
this.name = "";
}

// a private constructor
private Data(String n) {
this.name = n;
}
...
}


Anmerkung zu Update2:
Das Aushebeln geht allerdings nur, wenn theData nicht final ist.

grakaman
2004-11-30, 14:21:42
Na ich meinte eigentlich, wenn man in der/den Factory(s) die Instanz erstellt (deswegen heißt die wohl auch Factory), kann der, der die Klasse von "draußen" aufruft, festlegen mit welchen Konstruktor er sie initialisieren will. Das kann ich ja logischerweise nicht machen, wenn ich, wie lola, das Feld fest auf Klassenebene instanziere.

HellHorse
2004-11-30, 15:15:28
Theoretisch, aber wie schon gesagt, bloss beim ersten mal, wenn die singleton Instanz noch nicht erstellt wurde. Falls sie schon besteht, geht es nicht. Es gibt also keine Garantie, dass es hinaut und ist daher sehr gefährlich. Und eigentlich ist es ja die Idee einer factory Methode unter anderem von solchem Zeugs, wie welcher Konstruktor verwendet wird zu abstrahieren. ;)

Auf Klassenebene kannst du immer noch so was machen:

final class Data {
private String name;

// create the one and only object \u201cin store\u201d
private final static Data theData;

static {
Random rand = new Random();
if (rand.nextBoolean()) {
theData = new Data("a String");
} else {
theData = new Data();
}
}

// a private default constructor
private Data() {
this.name = "";
}

// a private constructor
private Data(String n) {
this.name = n;
}

....
}

grakaman
2004-11-30, 15:57:33
Aber genau das machst du doch mit der Factory Klasse. Du bräuchtest ja auch keine Factory Klasse, wenn du die Instanzierung des Feld auf Klassenebene durchführst. Dann könntest du das Feld gleich public machen.
Die Sache ist jetzt die, dass du das Singleton beliebig instanzieren willst. Das verbietet ja ein Singleton nicht, es geht ja nur darum für einen bestimmten Zeitraum nur eine Instanz zu garantieren. Aber genau so gut könntest du ja in der Klasse eine statische Methode implementieren, die die Instanz wieder löscht. Nur wenn du die Instanzierung in einer extra Methode vornimmst, (Factory) kannst du von Außerhalb das Singleton mit beliebigen Konstruktoren aufrufen. Und in deinem Bsp. mag das zwar so gehen, aber wenn du verschiedene Konstruktoren mit unterschiedlichen Parametern hast, dann müssen die ja auch irgendwo herkommen. Und dann für alle Parameter noch Instanzfelder deklarieren werden und ggf. noch Getter- und Setter-Methoden, anstatt die gleich nur einer Methode zu übergeben und dort die Instanzierung vorzunehmen, sieht mir das ziemlich umständlicher aus. Aber im Endeffekt kann ich das auch nur so sagen, wie ich es eben immer gelesen habe. Kann sein, dass man das eben bei Java anders macht.

HellHorse
2004-11-30, 16:47:45
Aber genau das machst du doch mit der Factory Klasse. Du bräuchtest ja auch keine Factory Klasse, wenn du die Instanzierung des Feld auf Klassenebene durchführst. Dann könntest du das Feld gleich public machen.
Ich kann ja auch später eine andere Klasse im static initializer instanzieren, kein Problem.

Die Sache ist jetzt die, dass du das Singleton beliebig instanzieren willst. Das verbietet ja ein Singleton nicht, es geht ja nur darum für einen bestimmten Zeitraum nur eine Instanz zu garantieren.
Nein, es darum sicherzustellen, dass von einer Klasse, bloss eine Instanz existiert. Schlag sonst noch einmal in Design Patterns nach.

Aber genau so gut könntest du ja in der Klasse eine statische Methode implementieren, die die Instanz wieder löscht.
Wenn dir deine Sprache erlaubt zu zählen wieviele Pointer auf ein Objekt existieren. Java tut es z.B nicht.

Nur wenn du die Instanzierung in einer extra Methode vornimmst, (Factory) kannst du von Außerhalb das Singleton mit beliebigen Konstruktoren aufrufen.
Sollst du aber nicht können. Darum ist der Konstruktor auch private. Das hat aber nix damit zu tun, wann das singleton kreiert wird. Ich verweise hier noch einmal auf Design Patterns.

Und in deinem Bsp. mag das zwar so gehen, aber wenn du verschiedene Konstruktoren mit unterschiedlichen Parametern hast, dann müssen die ja auch irgendwo herkommen. Und dann für alle Parameter noch Instanzfelder deklarieren werden und ggf. noch Getter- und Setter-Methoden, anstatt die gleich nur einer Methode zu übergeben und dort die Instanzierung vorzunehmen, sieht mir das ziemlich umständlicher aus.
Öh ja, der Konstruktor muss schon irgend woher kommen. Aber ich sehe nicht genau was du willst. Die Initialisation in der factory Methode statt im Konstruktor vornehmen oder was?

Aber im Endeffekt kann ich das auch nur so sagen, wie ich es eben immer gelesen habe. Kann sein, dass man das eben bei Java anders macht.
"Eigentlich" macht man es schon so, wie du es gepostet hast. Bloss muss es halt eben nicht lazy sein, damit es ein singleton ist.

grakaman
2004-11-30, 18:37:20
Der Konstruktor ist private, damit nicht beliebig viele Instanzen erstellt werden können, sondern nur eine. Das hat aber nun nichts damit zu tun, dass keiner von außerhalb das Objekt instanzieren bzw. verschieden initialisieren darf. Es muss lediglich gewährleistet werden, dass eben von der Klasse nur eine Instanz vorhanden ist. Wer da den Anfgang macht, ist ja unerheblich. Und das muss ja auch nicht die komplette App-Lebenszeit betreffen. Ich kann ja genau so ein System implementieren, dass auf ein Ereignis plötzlich komplexe Operationen macht und während dessen, sollen alle partizipierenden Objekte auf ein und die selbe Instanz zugreifen. Wenn die Operation fertig ist und ein neues Ereignis ausgelöst wird, möchte ich vielleicht ein anders initialisiertes Singleton Objekt, bei dem auch wieder alle partizipierenden Teilobjekte nur auf eine Instanz zugreifen sollen. Das ganze kann ja voll und ganz sequenziell ablaufen, dabei muss es sich doch nicht zwangsweise um irgend welche Remoteobjekte handeln, auf das mehrere Clients gleichzeitig zugreifen.

HellHorse
2004-11-30, 20:31:40
Der Konstruktor ist private, damit nicht beliebig viele Instanzen erstellt werden können, sondern nur eine.
Der Konstruktor ist private, damit "von aussen" keine Objekte generiert werden können.

Das hat aber nun nichts damit zu tun, dass keiner von außerhalb das Objekt instanzieren bzw. verschieden initialisieren darf.
Doch. Schau dir mal die Anmerkungen zur C++ Implementation in Design Patterns doch noch einmal an. Die sind diesbezüglich sehr klar.

Es muss lediglich gewährleistet werden, dass eben von der Klasse nur eine Instanz vorhanden ist. Wer da den Anfgang macht, ist ja unerheblich.
Eben, es muss nicht lazy sein.

Und das muss ja auch nicht die komplette App-Lebenszeit betreffen. Ich kann ja genau so ein System implementieren, dass auf ein Ereignis plötzlich komplexe Operationen macht und während dessen, sollen alle partizipierenden Objekte auf ein und die selbe Instanz zugreifen. Wenn die Operation fertig ist und ein neues Ereignis ausgelöst wird, möchte ich vielleicht ein anders initialisiertes Singleton Objekt, bei dem auch wieder alle partizipierenden Teilobjekte nur auf eine Instanz zugreifen sollen. Das ganze kann ja voll und ganz sequenziell ablaufen, dabei muss es sich doch nicht zwangsweise um irgend welche Remoteobjekte handeln, auf das mehrere Clients gleichzeitig zugreifen.
Und was machst du, wenn jemand irgendwo noch eine Referenz auf das "alte" singelton hat? Muss nicht remote sein.
Klar, in Smalltalk kannst du z.B. alle Pointer, die auf ein best. Objekt zeigen auf ein anderes Zeigen lassen wenn du bloss das referenzierte Objekt kennst. Java erlaubt dir solche Spässe nicht.

grakaman
2004-11-30, 23:08:53
Hier mal ein Zitat aus dem Buch "Enterprise Solutions Patterns using Microsoft.NET":

zu static Initialization bei Singleton:

...The only downside of this approach is that you have less control over the mechanics of the instantiaten. In the Design Pattern form, you were able to use a nonedefault constructor or perform other tasks before the instantiation..

Wie gesagt, es schreibt ja niemand vor für wie lange die Instanz erhalten sein soll, sie muss nur für einen bestimmten Zeitraum eben für alle Objekte gelten. Das kann für immer sein oder eben nur für einen bestimmten Zeitraum und danach erstellt die Software vielleicht eine veränderte Instanz, die jetzt plötzlich gelten muss. Bsp.: Ich habe einen Puffer, der bestimmte Daten sammeln soll und immer ausgeben soll. Vielleicht möchte ich ein Programm starten und zuvor den Puffer individuell anpassen (Größe, Ausgabetyp Konsole/Popup etc.). Oder es werden regelmäßig recht komplexe Operationen getriggert (sequenziell) und vor jedem Aufruf muss aber das alte Singleton gelöscht werden und ein anderes instanziert und initialisiert werden. Das ganze könnte natürlich auch parallel passieren, z.B. in verschiedenen App Domains (.NET) oder eben Prozessen. Oder man will einfach nur für einen bestimmten Zeitraum aus performancegründen die Instanzierung von mehreren gleichen Objekten verhindern. Es gibt da recht viele Anwendungsmöglichkeiten für ein Singleton, ich würde das nicht ganz so eng sehen.

HellHorse
2004-12-01, 12:30:48
Hier mal ein Zitat aus dem Buch "Enterprise Solutions Patterns using Microsoft.NET":
Es geht hier weder um "Enterprise Patterns" noch um .NET

zu static Initialization bei Singleton:

...The only downside of this approach is that you have less control
Natürlich ist statische Initialisierung weniger flexibel, das bestreite ich ja auch nicht. Aber gerade dadurch dass lola ja die statische Methode verwendet um auf das singleton zuzugreiffen anstatt das Feld public zu machen, behält er sich ja die Flexibilität das später zu ändern ohne dass es Änderungen in anderen Klassen erfordert.