# Daten speichern: Reflexion vs Serializable



## blauerninja (24. Apr 2012)

Hallo Leute,
um mich mit Java besser auszukennen, programmier ich an einem Spiel rum. Natürlich stellt sich da sofort die Frage nach dem Speichern und Laden. Prinzipiell habe ich 2 Methoden gefunden, wie das zu machen ist.

*Methode 1:* Man speichert das oberste Objekt eines Spiels, wo alles andere ja mit eingebunden ist. Dazu muss man natürlich das entsprechende Interface Serializable implementieren in jeder Klasse, da sonst Exception auftritt.

*Methode 2:* Man definiert in jeder Klasse eine Methode load(String) und save() (returns String) und ruft diese Methode beim Spielobjekt auf, die dann wiederum entsprechende load() save() Methoden bei den Objekten aufruft.


Ok, im Forum hab ich festgestellt, dass Methode 1 benutzt wird. Was spricht gegen Reflexion? Hat das jemand schon probiert und könnte eine Gegenüberstellung machen?


Frage zu Methode 1: kann man so machen, dass zb nicht alle Variablen gespeichert werden, sondern die sich im Spielverlauf ändern? Hab eine Klasse, wo die meisten Variablen berechnet werden, paar aus einer Datei gelesen werden (als Standardwert) und sich dann ändern können oder final sind. Jetzt wäre es unnötig die Variablen zu speichern, die man ohnehin beim Laden berechnen könnte, oder die final sind. Ist das überhaupt möglich?

Last but not Least:  bin noch hierrauf gestoßen und werd mich auch noch darüber informieren. Aber eure Meinungen würden mich interessieren.

Java-Forum Thread

Und zwar wird hier Deflater/Inflater/Input/OutputStream erwähnt
Wie bindet man dies am besten ein?

So, das waren viele Fragen, hoffe dass jemand Antworten hat. Vielen Dank im Vorraus und viel Spaß beim Proggen.


----------



## SlaterB (24. Apr 2012)

Variablen kann man als transient deklarieren, dann werden sie nicht serialisiert,

Serialisierung ist die einfache Standard-Speicherung auf Kosten von Overhead an Zeit und Platz, 
Problemen bei Änderungen der Klassen und vielleicht noch anderem

jede eigene Form der Speicherung, jedes andere Format, kann grundsätzlich effizienter sein, dafür muss man eben mehr programmieren,
Fehler und sonstige Sackgassen kann man bei sowas aber auch einbauen, da sollte man sich nichts vormachen,
einfache save()-Methoden die weitere save()-Methoden aufrufen haben z.B. die Gefahr von Endloskreisen, falls A B referenziert, und B wiederum A

---

Kompression ist doch gewiss ein Thema zu dem man Grundlagen im Internet findet,
da fällt mir pauschal nichts ein, womit ich dieses Posting noch sinnvoll bereichern könnte


----------



## blauerninja (24. Apr 2012)

Eine Endlossitiuation sollte eigtl nie passieren, da ein Spiel wie ein Baum aufgebaut ist. Ganz oben die Maps, Charaktere, diese haben ihrerseits Fähigkeiten, Items usw. Das heißt iwo ist ein Endknoten, zumindest sollte es so sein.


----------



## Volvagia (24. Apr 2012)

Ist Reflexion nicht der Zugriff auf Klassen über ihren Namen? So wie z. B. Something.class.newInstance()? Das was du beschrieben hast doch doch einfach ein Rekursiv/Iteratisch-gemischter Methodenaufruf, oder?

Wie SlaterB schon gesagt hat hat serialisieren sehr viele Nachteile, deshalb würde ich persönlich dir davon abraten. Eine Klassenveränderung wird sehr schwer bis fast unmöglich, und selbst wärend des testens bist du nicht in der Lage Werte zu verändern.

Was vielleicht noch erwähnt werden sollte ist das Speichern per Datenbank (z. B. H2)
Du schreibst einen Datenbankcontroller, der z. B. die Methode save() hat.

Diese ruft saveCharacters() auf, in dem du z. B. alle Chars. in einer Schleife durchgehst und einträgst:


```
id	name		hp	maxhp
0	WarriorA	50	50
1	HunterA		20	25
2	WarriorB	60	85
```

etc. In jeder der Schleifen des Chars gehst du alle Items durch:



```
owner_id	name		equipmed	duration	maxduration
0		knief		true		100		100
0		buckler		true		100		100
0		lethershoes	false		100		85
1		crossbow	true		100		30
```

Usw. Das erleichtert Änderungen, da du bspw. bei Umstrukturierungen nur das Ziel der Speicherung der geladenen Daten ändern musst (bzw. die Herkunft wärend des Abspeicherns), deshalb sind Klassenänderungen davon weitgehend nicht betroffen und du kannst auch später von Hand Daten ändern.

Der Nachteil von Datenbanken ist, dass sie nur Datensätze und keine einzelnen Daten speichern können. Aber über Umwege geht das auch, z. B. wenn in eine Tabelle eine Auflistung aller Savegames kommt (um die Savegames mit den Datensätzen zu verknüpfen). Hier können einzelne Dinge in zusätzlichen Spalten gespeichert werden. Finde ich zwar nicht schön, ist aber möglich.


----------



## Landei (24. Apr 2012)

blauerninja hat gesagt.:


> Prinzipiell habe ich 2 Methoden gefunden, wie das zu machen ist.
> 
> *Methode 1:* Man speichert das oberste Objekt eines Spiels, wo alles andere ja mit eingebunden ist. Dazu muss man natürlich das entsprechende Interface Serializable implementieren in jeder Klasse, da sonst Exception auftritt.
> 
> *Methode 2:* Man definiert in jeder Klasse eine Methode load(String) und save() (returns String) und ruft diese Methode beim Spielobjekt auf, die dann wiederum entsprechende load() save() Methoden bei den Objekten aufruft.



Methode 3: Man verwendet eines der Standardformate wie Json, XML u.s.w., wofür es fertige Bibliotheken gibt (Java bringt z.B. JAXB für XML schon mit), die das Lesen und Schreiben übernehmen. Die Datei kann dann im Gegensatz zu Serializable von dir auch verstanden und sogar manipuliert werden, wofür du bei der Fehlersuche und beim Testen bestimmt einmal dankbar sein wirst. Nachteil ist, dass du je nach Format unterschiedliche Annotationen an den Klassen brauchst, die die Details der Speicherung regeln, und die Klassen (wie bei Serializable) auch gewissen Einschränkungen unterliegen - so hat man z.B. bei JAXB Probleme mit zyklischen Abhängigkeiten.


----------



## blauerninja (24. Apr 2012)

@Landei: ich mach es momentan über Reflexion. Am besten ich erklärs an einem Beispiel, dann kann man darüber besser diskutieren.

Also: ich habe eine abstrakte Klasse Move, welche die Oberklasse für sämtliche Attacken ist. Enthalten sind hier einige Konstanten, wie zb: ID, Beschreibung (die ja immer gleich bleiben) und einige Variablen wie zB: Stärke, Ap und MaxAP (wie oft kann ich Attacke verwenden). Ich definiere auch sämtliche Methoden wie getPower() usw. zentral in dieser Klasse.

Die Unterklassen wie zb: final class Push extends Move besitzen eine Reihe statischer Konstanten, die im Konstruktor an die Oberklasse weitergegeben werden und dort die Variablen und Konstanten setzen. Einige Konstanten erhalten ihre Werte aus einer Textdatei (zb: Beschreibung), sodass diese in andere Sprachen transportiert werden können. Alles andere soll dem User unzugägnlich sein. Ok, bestimmt stellt ihr euch die Frage, wozu nicht gleich alle Variablen in eine 2. Datei reintun und verschlüsseln.

Dazu folgendes: manche Attacken wie zb: Push berechnen die Stärke, sprich ich überschreib getPower(). Also brauche ich Klassen für die Attacken. Also kann ich gleich auf eine 2. Textdatei verzichten und die Parameter in der Klasse definieren.

Nun zu save(): diese Instanzmethode der Klasse Move erzeugt einen String bestehend aus ID, Klassenname (zb: Push), AP, MaxAP. Die Werte werden durch ein Separatorzeichen von einander getrennt, sodass beim Auslesen Eindeutigkeit besteht. Dieser String wird returnt.
Der Speichervorgang soll im Hauptprogramm eingeleitet werden und dann sich zu den einzelnen Knoten durchhangeln, die Strings einsammeln und in eine Datei speichern. Kann man dann noch verschlüsseln, um Manipulation zu vermeiden.

Zu load(): ähnlich wie save() wird vom Hauptprogramm die statische Methode load() aufgerufen und die Knoten bekommen die Strings zugeteilt, die sie vorher erzeugt haben. Teile des Strings können sie weiter an die Unterknoten leiten. Im Beispiel: es wird load() der Klasse Move aufgerufen und aus dem String werden die ID, Klasse, AP und MaxAP ausgelesen. Über Reflexion erzeuge ich nun ein neues Objekt über den Parameter Klasse (ZB: Push) und übergebe ihm die anderen 3 Werte. Wie man sieht, brauch ich in Push 2 Konstuktoren, einen public Push() zum Instanziieren und einen protected Push(args) für Load. Auch im Load-Konstruktor von Push werden die Übergebenen Parameter (gemeinsam mit den statischen Klassenvariablen/Konstanten) an Move weitergeleitet. Hier hab ich wieder einen Load-Konstruktor, der die ganzen Werte nun setzt. Diesen Konstruktor brauch ich natürlich, da zb die ID nicht neu berechnet werden soll etc.

Das ist mein Ansatz. Somit kann ich eine manipulierbare XML-Datei nicht gebrauchen. Eine Datenbank muss auch verschlüsselt werden, nehm ich an. Das wäre auch eine Möglichkeit, da bei mir die Struktur schon ähnlich zu einer Datenbank ist.

@Volvagia: ich löse das beim Reflexion so:

```
Class<?> moveClass = Class.forName(values[1]); // Class ist aus java.lang.reflect-Package)
    Class<?>[] parameters = {Class.forName("java.lang.String"), Integer.TYPE, Integer.TYPE}; // Parameter: ID, AP, MaxAP
    Constructor<?> constructor = moveClass.getDeclaredConstructor(parameters);
    Object[] arguments = {values[0], Integer.parseInt(values[2]), Integer.parseInt(values[3])}; // values wird durch den load-String bestimmt, wird gesplittet
    output = (Move)constructor.newInstance(arguments);
```

Was meint ihr dazu? Ich hab noch gar keine Erfahrung in diesem Bereich. Diese Vorgehensweise erschien mir als die Sinnvollste. Wie würdet ihr das lösen?


----------



## Landei (25. Apr 2012)

Das Thema ist ziemlich komplex, und eine selbstgestrickte Lösung wird hier immer eingeschränkter und fragiler sein als eine bewährte Bibliothek. Reflection ist ein Holzhammer, den man nicht aus Dudeldei auspacken sollte, denn im Prinzip ist es das Eingeständnis, dass man etwas tun möchte, das man mit der Sprache nicht auf normalen Wege ausdrücken kann, und deshalb auf Typsicherheit, Kapselung u.s.w. verzichten muss.


----------



## blauerninja (25. Apr 2012)

Hm, das Speichern scheint mir klar zu sein. Wie erzeugt man bei der Möglichkeit mit XML, Datenbank Objekte? Dort stehen eigtl  nur die gespeicherten Werte. Scheint dann wieder auf Reflexion zurückgeführt zu sein.

Anmerkung: es interessiert mich weniger mit welchem Framework und so, sondern wie die Frameworks das intern managen. Ist jetzt etwas, was bestimmt nur wenige wissen. Erwarte dazu eigtl keine Antwort mehr.

Eine letzte Frage: wie speichert ihr? Datenbank mittels Hibernate u.ä. oder eigene Textdatei, Serialisierung?


----------



## SlaterB (25. Apr 2012)

um es noch einmal zu erwähnen, interessant genug:
Serializable IST Reflection, IST der Standardweg der einfach die Klassen so wie sie aufgebaut sind Feld für Feld durchgeht und die Werte speichert usw.


----------

