2009-11-29 12 views
7

Gibt es eine Möglichkeit, Flyweight-Objekte mit der Ruhezustandszuordnung zu verwenden? Mein Datenmodell enthält viele Objekte, die identisch sind. Anstatt eine separate Instanz für jedes dieser Objekte zu verwenden, möchte ich das Flyweight Design Pattern verwenden und immer auf dasselbe physische Objekt verweisen. Wie erreicht man dies im Winterschlaf?Hibernate und Flyweight

Btw. optimieren alle JVMs die Verwendung von Strings auf eine Weise, dass dieselbe Zeichenkette, wenn sie mehrfach verwendet wird, immer die gleiche physische Instanz ist?

Antwort

3

Es kommt darauf an.

Für readonly Werte können Sie leicht ein flyweight-Muster implementieren, indem Sie einen benutzerdefinierten UserType erstellen, der Objekte jedes Mal aus einem Pool statt neuer Instanzen zurückgibt.

Für Entitäten Hibernate ist standardmäßig vernünftig und möchte konsistent zwischen Transaktionen sein und wird daher keine Entitäten zwischen Sitzungen teilen, um Race-Bedingungen Ihrer Daten zu vermeiden - und ich glaube nicht, dass Sie das wollen.

Aber falls es ist (und das ist völlig nicht zu empfehlen, ohne wirklich zu wissen, was Sie tun) können Sie Interceptor.getEntity() implementieren, die für das Zwischenspeichern auf der zweiten Ebene gedacht ist. In dieser Methode können Sie eine Entität (sogar einige, die von anderen Sitzungen geteilt werden) zurückgeben, und Sie haben effektiv ein Fliehgewichtsmuster für Ihre Entitäten.

ABER ich empfehle sehr dagegen für die Konsistenz Ihrer Daten - viel besser, um tatsächliche unveränderliche Fliegengewicht Werte von Entitäten als auch versuchen und Fliegengewicht der tatsächlichen Entitäten haben.

+0

Der Benutzertyp ist eine gute Idee, um den gemeinsamen Status der Fliegengewicht-Instanzen abzubilden. (Hibernate und JDBC werden immer noch viele Instanzen initiieren. Dazu gibt es keinen Weg. Zumindest werden sie ziemlich schnell zur Garbage Collection.) Kann ein CompositeUserType den gesamten Common State in einem Objekt repräsentieren? –

+0

Ja, ein zusammengesetzter Benutzertyp könnte das wahrscheinlich tun, aber warum hat die Entität überhaupt eine Entität? Sei einfach von Anfang an ein Wertobjekt (Komponente). –

0

Wenn Ihre Objekte die Gleichheit nach Identität implementieren, wird in Hibernate nur die einzelne Instanz diesem Primärschlüssel zugeordnet. Ich glaube nicht, dass es genau dieselbe Idee wie Flyweight ist, aber der Punkt ist, dass Sie nicht viele Instanzen des gleichen Hibernate-Objekts haben werden.

2

Ja, Sie können das Flyweight-Muster mit Hibernate implementieren.

Das Flyweight-Muster ist eine Möglichkeit, die Speichernutzung zu minimieren pro Instanz. Die Strategie besteht darin, so viele Zustände wie möglich zwischen Fliegengewicht-Instanzen zu teilen. In Ihrem Fall ist der gemeinsam nutzbare Status alles außer dem Hibernate-Objektbezeichner und einem zusätzlichen Status zur Beibehaltung der Objektidentität.

Jede Fliehgewicht-Instanz benötigt eine eigene object identity. Der zusätzliche Status ist die Art und Weise, Identität zu implementieren, um zwischen Objekten mit gemeinsamem Status zu unterscheiden.

public boolean equals(Object obj){ 
    Fly other; [..]//check type 
    //null checks ommitted 
    return other.myState.equals(myState) && other.commonState.equals(commonState); 
} 

Wenn die Objektidentität zwischen Instanzen gemeinsam genutzt wird winter würden alle physischen Instanzen (Referenzen) als die gleiche Instanz interpretieren. Hibernate verwendet die equals-Methode, um die Objektidentität zu überprüfen, und Ihre gleichwertige Implementierung müsste (! a.equals(a) == true) zurückgeben, was illegal ist. Equal muss reflexiv sein. Wenn Sie diesen Vertrag brechen, werden alle Bibliotheken, die vom Vertrag abhängig sind, unterbrochen (Sammlungen, Ruhezustand usw.).

Sie können die Methode equal nicht implementieren, indem Sie den Objektnamen des Hibernate-Objekts verwenden, um zwischen Objekten zu unterscheiden. Dies würde die Objektidentität von dem Persistenzzustand abhängig machen (persistent oder transient).

Eine Möglichkeit, den gemeinsamen Status im Ruhezustand zu modellieren, ist eine Eins-zu-Viele-Verknüpfung zwischen Objekten mit gemeinsam genutztem Status und Objekten mit Fliegengewicht. (Vielleicht hat jemand eine Idee, wie man die Daten abbildet, ohne zwei Tabellen zu verbinden?)

String: Nur internalized strings wird geteilt. Dies ist die meiste Zeit nicht die beste Lösung. Es eignet sich für Symbole (Klassenname, Methodenname usw.). Die internalisierten Strings werden niemals von Müll gesammelt und Sie müssen eine String-Instanz haben, die sowieso Müll gesammelt werden soll new String("..").intern(). Es werden keine Zuordnungen gespeichert. Es gibt nur den geringfügigen Vorteil, dass die Basiszeichenfolge eine GC-Generation nicht überlebt oder auf dem Stapel zugeordnet werden könnte (mit der Escape-Analyse in Hotspot aktiviert und anwendbar).

1

optimieren alle JVMs die Verwendung von Strings so, dass dieselbe Zeichenfolge mehrmals verwendet wird, wenn immer dieselbe physische Instanz verwendet wird?

Ich bezweifle das sehr.In der gleichen Klassendatei, wenn definiert wie:

String s1 = "Yes"; 
String s2 = "Yes"; 

haben Sie wahrscheinlich s1 == s1.

Aber wenn man wie:

String x = loadTextFromFile(); // file contains es 
StringBuilder b = new StringBuilder(); 
s2 = b.append("Y").append(x).toString(); // s2 = "Yes" 

Ich glaube nicht, dass die Laufzeit überprüfen wird und vergleichen Sie alle geladenen Strings den Rückgabewert von ihnen Builder.

Vergleichen Sie also Objekte immer mit equals(). Das ist ein guter Rat, denn jeder gute Wert beginnt mit:

if (this == o) { 
    return true; 
} 
+0

Es geht nicht um Vergleich, sondern um Speicherverbrauch. Wie Thomas Jung erwähnt, können Sie die String.intern() -Methode verwenden, um den zugrunde liegenden String zu erhalten. Versuch es! – paweloque

+0

Was ich sagen wollte ist, dass, wenn ein String-Wert zur Kompilierzeit nicht bekannt ist, oder nicht vorberechnet werden kann, das Objekt (no ==) abweichen wird, auch wenn es zufällig den gleichen Wert hat. – extraneon