![]() |
Flexible(re) Ladeprozedur
Erstmal hoff ich, dass ich die richtige Spart erwischt hab. Es gibt ja kein extra Unterforum für Dateien & Co - falls doch, dann sponsort mir eine Brille :mrgreen:
Folgendes: Ich habe ein Tool das seit einiger Zeit entwickelt wird und wo ab und zu neue Versionen herauskommen (wer hätte es gedacht *g*). Da sich dadurch die Speicherstruktur ändert, muss ich ältere Dateien abwärts kompatibel einlesen können. Meine Datei enthält deshalb einen Integer für die Version und dann mein Array of Record. Eingelesen hab ich es bisher mit 'nem Filestream und einer case Abfrage nach dem Motto:
Delphi-Quellcode:
Das Prinzip sollte klar sein. Aber es hat einen großen Nachteil: Für jede neue Version muss ich alle cases anpassen, wenn sich die Speicherstruktur verändert hat.
case version of
1: Einlesen und an aktuellen Record anpassen 2: Einlesen und an aktuellen Record anpassen 3: Einlesen und an aktuellen Record anpassen 4: Direkt Einlesen, da aktuelle Version Daher hab ich mir momentan folgende Variante überlegt: 1. Datei in Filestream (fs) einlesen und Version auslesen 2. fs in Memorystream (ms) kopieren 3.
Delphi-Quellcode:
Ich hoffe der Pseudo-Code ist verständlich und vermittelt meine Idee. Mit der Variante muss ich immer nur einen neuen If-Block für eine neue Speicherstruktur einbauen, was das Handling vereinfacht. Ältere Versionen steigen entsprechend früher in den Code ein und werden angepasst.
if version=1 then
begin ms an Record von Version 2 (!) anpassen und in Temp-Memorystream (temp) kopieren temp wieder nach ms zurückkopieren version auf 2 setzen end; if version=2 then begin ms an Record von Version 3 (!) anpassen und in Temp-Memorystream (temp) kopieren temp wieder nach ms zurückkopieren version auf 3 setzen end; if version=3 then begin ms an Record von Version 4 (!) anpassen und in Temp-Memorystream (temp) kopieren temp wieder nach ms zurückkopieren version auf 4 setzen end; [...usw...] if version=4 then begin ms direkt in Speicher einlesen end; Allerdings muss ich mich so mit drei Streams herumschlagen die die Datei enthalten -> nicht gerade Ressourcenfreundlich. Hat mir da jemand Tipps/Verbesserungsvorschläge? Die Datei hat zwar nur 50K, aber hier gehts ums Prinzip ;) Oder gibts einen völlig anderen Weg? |
Re: Flexible(re) Ladeprozedur
Zitat:
ohne dass ich jetzt irgendeinen Einfluss auf die Einordnung durch die Moderatoren habe (außer vielleicht ich kann mich glaubhaft über deine Einordnung beschweren), ich würde nicht sagen dass es so direkt etwas mit dem Windows API zu tun hat (oder übersehe ich die API Zugriffe?). Fände ObjectPascal/Delphi-Language oder sogar sonstige Fragen passender. Aber ist ja auch egal! Zitat:
Hier solltest Du schauen, ob Du wirklich Geschwindigkeit durch die Verwendung eines MemoryStreams gewinnst. Schon das Umkopieren dürfte allerdings mehr Zeit kosten. Kann natürlich auch sein, dass Du es aus einem anderen Grund machst, aber eigentlich sollte alles auch mit einem FileStream möglich sein. Zitat:
Wenn dies der Fall ist, dann kannst Du sehr effezient einfach den ganzen Block aller Datensätze in ein entsprechendes Array einlesen. Je nach Versionsnummer erstellst Du einfach ein dyn. Array der entsprechenden Größe und des entsprechenden Typs. Die restliche Arbeit kannst Du dann einfach auf einem solchen Array ausführen. Deine Idee immer die Anpassung an die nächste Version vorzunehmen solltest Du natürlich genauso beibehalten. Der Unterschied liegt dann nur darin, dass Du diese Datensätze nicht extra aus einem Stream extrahierst und wieder dorthin zurückschreibst, sondern gleich mit einem festen Datentypen arbeitest. Das sollte die Arbeit deutlich vereinfachen. An sich muss jede Version dann zwei Methoden anbieten:
Und als Pseudocode:
Delphi-Quellcode:
So ungefähr würde ich es Dir vorschlagen. Die Idee ist hoffentlich auch hier klar. Du nimmst einfach einen Alias für den aktuellen Datentypen und gibt ein Array von diesem Typen als Ergebnis des Ladens zurück.
type
TTyp1 = record ... end; TTyp1Array = Array of TTyp1; TTyp2 = record ... end; TTyp2Array = Array of TTyp2; TTyp3 = record ... end; TTyp3Array = Array of TTyp3; TAktuellerTyp = TTyp3; TAktuellerTypArray = Array of TAktuellerTyp; function loadFromStream(const Stream: TStream): TAktuellerTypArray; var versionsNummer: Integer; begin ... case versionsNummer of 1: result := convert(readTyp1(Stream)); 2: result := convert(readTyp2(Stream)); 3: result := convert(readTyp3(Stream)); end; end; function readTyp1(const Stream: TStream): TTyp1Array; begin // ist klar! end; function readTyp2(const Stream: TStream): TTyp2Array; begin // ist klar! end; //... function convert(var typ1Array: TTyp1Array): TAktuellerTypArray; var typ2Array: TTyp2Array; begin // Umwandlung von typ1Array in ein Typ2 Array // .... // typ1Array wird nicht mehr benötigt, Speicher frei geben! setLength(typ1Array, 0); finalize(typ1Array); result := convert(typ2Array); end; // .... function convert(const typ3Array: TTyp3Array): TAktuellerTypArray; begin result := typ3Array; end; Für die aktuelle Version muss also nur das übergebene Array zurückgegeben werden. Kommt eine neue Version hinzu, erstellst Du den entsprechenden neuen Datentypen und änderst den Alias TAktuellerTyp entsprechend ab. Zudem musst Du convert für diesen Typen überladen und das convert der Vorgängerversion anpassen. Das Laden gehört dann natürlich auch noch in die case-Anweisung, aber an sich halten sich (hoffentlich) die Änderungen in Grenzen. Wie gesagt, der eigentliche Unterschied zu deinem Ansatz liegt hier nur darin, dass Du gleich mit Datentypen arbeitest und nicht den Umweg über zwei Streams gehst. Gruß Der Unwissende |
Re: Flexible(re) Ladeprozedur
Warum hat Gott die objektorientierte Programmierung erfunden.
Erstell Dir zu jeder Version eine Klasse, die sich ums Kovertieren kümmert. Willst Du ganz generisch halten, und dir die if-Abfragen (bzw. deine Case Strukur was genau das gleiche ist nur in grün), so kannst Du mit Metaklassen arbeiten. Ich hatte diese Problematik auch mal erfragt, und in ![]() Im Grunde wird aufgrund der Version, die als Integer irgendwie vorliegen könnte, die entsprechende richtige Klasse instanziert. |
Re: Flexible(re) Ladeprozedur
Zitat:
Zitat:
Delphi-Quellcode:
Gespeichert wird einfach das Account-Array. Ändern tun sich in den Versionen meist die Anzahl der Account und die Inhalte des TPlaneten-Records.
type
TPlaneten = packed Record [statische Arrays, Strings und Daten] end; TAccount = packed Record Planet: array[0..12] of TPlaneten; [statische Arrays, Strings und Daten] end; var Account:Array[0..12] of TAccount; Zitat:
@Jelly Zitat:
Zitat:
Weil bisher klingt das auch so, als ob ich für jede Version das Array of Record erstellen müsste ... und das scheint mir mehr Aufwand, als mein bisheriger Weg. |
Re: Flexible(re) Ladeprozedur
Kuck mal auf Luckie's Homepage, da gibts was zu Klassen.
Prinzipiell wirst du gleiche Sachen in einer Basisklasse definieren, und versionsabhängige Sachen in davon abgeleiteten Klassen. Etwa so, in sehr knapper Form:
Delphi-Quellcode:
Nutzen geht dann so:
type
TBasis = class public Planetname : string ; // ... constructor Create (Filename : string) ; procedure Load ; virtual ; end; TPluto = class (TBasis) public procedure Load ; override ; end; TVenus = class (TBasis) public procedure Load ; override ; end;
Delphi-Quellcode:
Dadurch wird dann jeweils die korrekte Load Prozedur aufgerufen.
var
Pluto, Venus : TBasis ; begin Pluto := TPluto.Create ('file.txt') ; Venus := TVenus.Create ('file.txt') ; Pluto.Load ; Venus.Load ; end. |
Re: Flexible(re) Ladeprozedur
Mit den Planetennamen in deinem Beispiel meintest du die verschiedenen Versionen meiner Datei, oder? Weil das ist etwas unglücklich gewählt ;) - im Prinzip verändern sich ja nicht nur die Planeten, sonder auch mal die Accounts bzw. es kommen neue Accounts (+Planeten) dazu.
Wenn ich dein Beispiel (und Luckies Tutorial) recht verstanden habe, wäre ich mit der Lösung zwar elegant, aber wieder am Anfang. Weil die einzelnen Konvertierungen werden ja nicht mehr nacheinander, sondern wieder 'auf einmal' ausgeführt. Und somit müsste ich bei einer neuen Version wieder alle .Load Prozeduren ändern, damit sie an das aktuelle record angepasst werden. :( |
Re: Flexible(re) Ladeprozedur
Natürlich musst du die load Prozedur bei einer neuen Version neu schreiben. Wie soll das auch anders gehen :gruebel:
|
Re: Flexible(re) Ladeprozedur
Ich glaub du hast mich falsch verstanden. Sicher muss ich die Load Prozedur erweitern. Aber in meiner obigen Variante muss ich nur die If-Abfrage und das Einlesen der neuen Version hinzufügen.
Bei meiner alten und bei deiner Variante - sofern ich dich recht verstanden habe - müsste ich alle Load-Procedures für jede Version updaten... verstehst du mein Problem? :stupid: |
Re: Flexible(re) Ladeprozedur
Gemeinsamheit zwischen den Version kannst du in der Basisklasse implementieren. Da liegt ja gerade der Vorteil von OOP.
|
Re: Flexible(re) Ladeprozedur
Die Gemeinsamkeiten kann ich aber nicht voraussehen. Mal ändert sich ein Variablentyp, dann wird hier mal ein Array erweitert oder dort ein ganz neues Array eingefügt ...
Im Prinzip sind Klassen eine gute Idee für die normale Verwendung in meinem Tool. Aber für die Ladeprozedur scheinen sie mir ungeeignet, da ich immer irgendwie eingeschränkt bin. :| |
Alle Zeitangaben in WEZ +1. Es ist jetzt 19:54 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz