Was ist ein gutes Design, um die Abwärtskompatibilität von Dateien zwischen verschiedenen Softwareversionen zu ermöglichen?

13

Was ist ein gutes Design, um die Abwärtskompatibilität eines Dateityps zwischen verschiedenen Softwareversionen zu ermöglichen?

Beispiel: Wie kann Microsoft Word 2007, 2010 und 2013 usw. in alle geöffneten DocX-Dateien übernehmen? Unterschiedliche Editionen können jedoch mehr oder weniger Daten speichern und die Daten auf leicht unterschiedliche Weise speichern, und zwar alle im selben Dateityp und a Datei, die in einer Version gespeichert ist, kann in einer anderen Version geöffnet werden, aber bestimmte Elemente der Datei sind in älteren Versionen möglicherweise nicht verfügbar?

Ich meine, der wirklich offensichtliche Weg ist, so etwas zu haben

private string openfile(string filename)
{
    File.Open(filename)

    ... some logic that gets a header from the file that will never change

    switch (fileversion)
        case 2007:
            .....
        case 2010
            .....
        case 2013
            .....
}

aber das scheint unglaublich monolithisch, nicht sehr erweiterbar und führt wahrscheinlich zu viel kopiertem / eingefügtem Code.

Daher habe ich mir überlegt, für alle Versionen eine Basisschnittstelle zu verwenden, die die unveränderlichen Strukturen definiert, z. B. den Header, die in der Datei vorhanden sein müssen, und Methoden, die für die Serialisierung / Deserialisierung verfügbar sein müssen, und dann jeweils eine Mehrfachvererbung Die Klasse der neuen Version, die die Schnittstelle implementiert, erbt die alte Version und überschreibt nur die Änderungen, da die Datei zum größten Teil identisch ist.

Die Struktur der Datei interessiert mich nicht wirklich, da bereits entschieden wurde, XML zu verwenden, und das anfängliche Schema im Großen und Ganzen bereits festgelegt ist. Es wird jedoch zweifellos in Zukunft Änderungen geben, und ich möchte den Code nur so gestalten können, dass diese Änderungen problemlos berücksichtigt werden können.

JJBurgess
quelle
6
Sie sollten das Dateiformat so gestalten, dass nicht nur fehlende Informationen ignoriert werden, da die Quelle aus einer früheren Version stammt, sondern auch Informationen, die nicht erwartet werden, weil die Quelle aus einer neueren Version stammt. Wenn Sie von vorne anfangen, bitte, bitte, bitte auch aufwärtskompatibel . Es ist fast kein zusätzlicher Aufwand und verdoppelt den Nutzen Ihrer Software.
Kilian Foth
Wissen Sie beim Öffnen immer im Voraus (z. B. anhand des Headers), mit welcher Dateiversion Sie es zu tun haben? Um eine weitere Anfrage zu stellen, überprüfen Sie bitte, ob beschädigte oder bösartige Dateien vorliegen, und lassen Sie sie keine Probleme verursachen. Ihre Sysadmins werden es Ihnen danken :).
cxw
1
Ja, die Versionsnummer befindet sich immer im Dateikopf und das Kopfzeilenformat wird sich nie ändern. Wir gehen von der Idee aus, dass Dateien, die zwischen kleineren Softwareversionen erstellt wurden, kompatibel sein sollten, dh eine in Version 1.1 erstellte Datei kann in Version 1.2 geöffnet werden und umgekehrt, obwohl in Version 1.1 möglicherweise einige Funktionen von Version 1.2 fehlen, in größeren Versionen jedoch Bricht die Vorwärtskompatibilität, sodass in v2 geschriebene Inhalte nicht in v1 geöffnet werden, in v1 geschriebene Inhalte jedoch in v2.
JJBurgess
Und was die Korruptionssache betrifft, enthalten die Dateien DSL, und das Programm, das sie öffnet / schließt, ist eine benutzerdefinierte interne IDE / ein eigener Compiler. Diese werden nicht in die Nähe einer Produktionsumgebung gelangen, sodass sich der Administrator keine Sorgen machen muss.
JJBurgess

Antworten:

10

Vielleicht sehen Sie sich das PNG-Dateiformat an und wie es mit der Versionskompatibilität umgeht. Jeder Block hat eine ID, die beschreibt, um welche Art von Block es sich handelt, und einige Flags teilen der Software mit, was zu tun ist, wenn diese ID nicht verstanden wird. Zum Beispiel "Sie können die Datei nicht lesen, wenn Sie diesen Block nicht verstehen" oder "Sie können die Datei lesen, aber nicht ändern" oder "Sie können die Datei ändern, aber Sie müssen diesen Block löschen". Aus Gründen der Abwärtskompatibilität muss Ihre Software nur die Situation bewältigen, in der erwartete Daten nicht vorhanden sind.

gnasher729
quelle
Großartige Idee! Das PNG-Format basiert auf Funktionen und nicht auf Versionen. Dies bedeutet jedoch, dass sich das Basisformat niemals ändern darf. (dh der Header definiert die Funktion.)
Florian Margaine
Das ist interessant. Ich lese gerade die Dateispezifikation durch. Ich mag die Idee der kritischen und
Hilfskomponenten
3

Eine Möglichkeit hierfür ist die Verwendung einer Basisklasse und einer Schnittstelle mit den Grundfunktionen für Ihre Dateiverwaltung. Verwenden Sie dann Klassen für jede Version, die von der Basisklasse ausgeht, um alle versionsspezifischen Fälle zu behandeln. Funktionen, die sich ändern können, können in Ihrer Basisklasse von Abstract virtuell sein, wenn es nur versionsspezifische Implementierungen gibt. Wenn Sie eine Klasse zum Verarbeiten der Datei benötigen, verwenden Sie eine Factory, die die versionsspezifische Implementierung der Dateiverarbeitungsschnittstelle abruft.

Peer
quelle
Mein einziges Problem dabei ist, dass Sie die versionsspezifische Implementierung für jede nachfolgende Revision duplizieren müssen. Nehmen wir an, Sie haben drei Basisklassenmethoden: ReadNames (), ReadAges () und ReadAddresses (). In V2 der Klasse nehmen Sie eine Änderung an ReadAges () vor. Wenn Sie in V3 eine Änderung an ReadNames () vornehmen, wenn alle Ihre versionsspezifischen Klassen von der Basis erben, verlieren Sie Ihre V2-Änderungen, oder Sie müssen die Änderungen aus V2 kopieren / einfügen auch in die V3-Implementierung.
JJBurgess
1
Die Implementierung von readages kann eine andere Klasse aufrufen, die die tatsächliche Implementierung zum Lesen des Alters für diese Version enthält. Wenn Sie Ihre Klasse erstellen, werden Sie mehr Schnittstellen / Fabriken konfigurieren als Programmieren.
Peer
2

Ich habe dies mit XML gemacht und es funktioniert gut:

Erlauben Sie einfach jedem Element in Ihrem Dokument, Attribute und Unterelemente zu haben (und wenn die Reihenfolge nicht wichtig ist - in beliebiger Reihenfolge). Ab der ersten Programmversion: Ignorieren Sie beim Lesen des Dokuments Attribute und Unterelemente, die Sie in der aktuellen Version nicht kennen.

Wenn Sie in Zukunft neue Funktionen zu einer neuen Programmversion hinzufügen, fügen Sie ein Attribut oder ein Unterelement hinzu. Ältere Versionen ignorieren dies. Die neue Version sollte die Dringlichkeit von Attributen oder Unterelementen prüfen und damit umgehen.

Zum Beispiel haben Sie einige Artikel mit Texten:

<item text="Hello, world!"/>

Und in einer neueren Version möchten Sie dem Element Farbe hinzufügen, damit Sie das Attribut hinzufügen color:

<item text="Hello, world!" color="008000"/>

In älteren Versionen werden colorAttribute beim Öffnen des Dokuments einfach ignoriert . Neue Versionen prüfen, ob das colorAttribut vorhanden ist, und weisen die Standardfarbe zu.

Mit dieser einfachen Lösung sind Sie sowohl vorwärts als auch rückwärts kompatibel.

user3123061
quelle
Das kleine Problem bei dieser "einfachen" Option ist, dass Sie beim Speichern des Dokuments alle unerwarteten Attribute entfernen (oder unverändert lassen). Wie in anderen Antworten erwähnt, bestimmt eine bessere Lösung zumindest in einer versionsunabhängigen Weise, ob ein Attribut gelöscht, beibehalten oder das Dokument für die Versionen schreibgeschützt werden soll, die es nicht verstehen.
Mark Hurd
@ Mark Hudr: Ja, ich gehe stillschweigend davon aus, dass Abwärtskompatibilität ein Muss ist und Aufwärtskompatibilität ein Bonus ist. Wenn jemand ein neues Dokument in einer alten Version von Anwendungen öffnet, sollte er nicht überrascht sein, dass er beim Speichern etwas verloren hat, was in einer alten Anwendung noch nicht sichtbar ist. Zusätzliche Logik scheint mir überarbeitet.
user3123061