Jaki jest dobry projekt umożliwiający wsteczną zgodność plików między różnymi wersjami oprogramowania?

14

Jaki jest dobry projekt umożliwiający wsteczną zgodność typu pliku między różnymi wersjami oprogramowania?

Na przykład, w jaki sposób Microsoft uzyskuje słowo 2007, 2010 i 2013 itd. ... do wszystkich otwartych plików docx, ale różne wersje mogą zapisywać więcej / mniej danych i zapisywać dane w nieco inny sposób, wszystkie w tym samym typie pliku i plik zapisany w jednej wersji można otworzyć w innej, ale niektóre elementy pliku mogą nie być dostępne w starszych wersjach?

To znaczy, naprawdę oczywistym sposobem na zrobienie tego jest posiadanie czegoś takiego

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
            .....
}

ale wydaje się to niewiarygodnie monolityczne, niezbyt rozszerzalne i może prowadzić do dużej ilości kopiowanego / wklejanego kodu.

Zastanawiałem się więc nad użyciem podstawowego interfejsu dla wszystkich wersji, które definiują niezmienne struktury, takie jak nagłówek, które muszą być obecne w pliku, oraz metody, które muszą być dostępne do serializacji / deserializacji, a następnie wielokrotnego dziedziczenia, aby każda klasa nowej wersji, która implementuje interfejs, dziedziczy starą wersję i zastępuje tylko rzeczy, które uległy zmianie, ponieważ plik będzie w większości taki sam.

Tak naprawdę nie przejmuję się strukturą pliku, ponieważ już zdecydowano, że będziemy używać XML, a początkowy schemat jest, ogólnie rzecz biorąc, już ustalony. Jednak bez wątpienia w przyszłości będą do niego wprowadzane zmiany, a ja po prostu chcę móc zaprojektować kod w sposób ułatwiający dostosowanie się do tych zmian.

JJBurgess
źródło
6
Powinieneś zaprojektować format pliku, aby nie tylko ignorował brakujące informacje, ponieważ źródło pochodzi z wcześniejszej wersji, ale także informacje, których nie oczekuje, ponieważ źródło pochodzi z nowszej wersji. Jeśli zaczynasz od zera, prosimy również o kompatybilność do przodu . To prawie żaden dodatkowy wysiłek i podwaja użyteczność twojego oprogramowania.
Kilian Foth,
Czy na otwartej przestrzeni zawsze będziesz wiedział z góry (np. Z nagłówka), z którą wersją pliku masz do czynienia? Ponadto, aby złożyć kolejne żądanie, sprawdź, czy nie ma uszkodzonych lub złośliwych plików i nie pozwól im powodować problemów. Twoi administratorzy będą Ci wdzięczni :).
cxw
1
Tak, numer wersji zawsze będzie w nagłówku pliku, a format nagłówka nigdy się nie zmieni. Myślimy, że pliki utworzone między drobnymi wersjami oprogramowania powinny być kompatybilne, tzn. Plik utworzony w wersji 1.1 może być otwarty w wersji 1.2 i odwrotnie, chociaż niektóre funkcje z wersji 1.2 mogą być niedostępne w wersji 1.1, ale główne wersje zepsuje przekazywanie zgodności, więc rzeczy napisane w wersji 2 nie otworzą się w wersji 1, ale rzeczy napisane w wersji 1 otworzą się w wersji 2.
JJBurgess
A jeśli chodzi o problem z korupcją, pliki zawierają DSL, a program otwierający / zamykający je jest niestandardowym wewnętrznym kompilatorem IDE /. Nie będą się nigdzie zbliżać do środowiska produkcyjnego, więc administrator nie musi się martwić.
JJBurgess

Odpowiedzi:

10

Możesz rzucić okiem na format pliku PNG i sposób, w jaki obsługuje on zgodność wersji. Każdy blok ma identyfikator opisujący, jaki to jest blok, i ma pewne flagi, które informują oprogramowanie, co zrobić, jeśli nie może zrozumieć tego identyfikatora. Na przykład „nie możesz odczytać pliku, jeśli nie rozumiesz tego bloku”, lub „możesz odczytać plik, ale go nie modyfikować”, lub „możesz zmodyfikować plik, ale musisz usunąć ten blok”. Aby zapewnić zgodność z poprzednimi wersjami, oprogramowanie musi jedynie poradzić sobie z sytuacją, gdy nie ma oczekiwanych danych.

gnasher729
źródło
Świetny pomysł! Format PNG zależy od funkcji, a nie od wersji. Oznacza to jednak, że podstawowy format nigdy się nie zmienia. (tj. nagłówek definiujący funkcję.)
Florian Margaine
To interesujące. W tej chwili czytam specyfikację pliku. Podoba mi się pomysł na fragmenty krytyczne i pomocnicze i mogę spróbować to wykorzystać.
JJBurgess
3

Można to zrobić za pomocą klasy bazowej i interfejsu z podstawowymi funkcjami obsługi plików. Następnie użyj klas dla każdej wersji, które wychodzą z klasy podstawowej, aby obsłużyć wszystkie przypadki specyficzne dla wersji. Funkcje, które można zmienić, mogą być wirtualne w twojej podstawowej klasie abstrakcji, jeśli istnieją tylko implementacje specyficzne dla wersji. Gdy potrzebujesz klasy do obsługi pliku, skorzystaj z fabryki, która pobiera specyficzną dla wersji implementację interfejsu obsługi plików.

par
źródło
Moim jedynym problemem jest to, że w każdej kolejnej wersji skończy się powielanie implementacji specyficznej dla wersji. Powiedzmy, że masz trzy podstawowe metody klas: ReadNames (), ReadAges () i ReadAddresses (), aw wersji 2 klasy wprowadzasz zmiany w ReadAges (). Jeśli w V3 zdecydujesz się na zmianę ReadNames (), jeśli wszystkie klasy specyficzne dla twojej wersji dziedziczą po podstawie, utracisz zmiany w V2 lub będziesz musiał skopiować / wkleić zmiany z V2 również do implementacji V3.
JJBurgess
1
Implementacja odczytów może wywoływać inną klasę, która przechowuje faktyczną implementację sposobu odczytu wieku dla tej wersji. Stworzenie twojej klasy będzie bardziej konfiguracją interfejsów / fabryk niż faktyczne programowanie.
peer
2

Zrobiłem to z XML-em i działa dobrze:

Po prostu zezwól, aby dowolny element w dokumencie miał dowolne atrybuty i dowolne podelementy (a gdy kolejność nie jest ważna - w dowolnej kolejności). Począwszy od pierwszej wersji programu - podczas czytania dokumentu ignoruj ​​atrybuty i podelementy, których nie znasz w bieżącej wersji.

W przyszłości, gdy dodasz nową funkcję do nowej wersji programu, dodaj atrybut lub podelement. Starsze wersje zignorują to. Nowa wersja powinna sprawdzać obecność atrybutu lub podelementu i obsługiwać go.

Na przykład masz kilka elementów z tekstami:

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

W nowszej wersji chcesz dodać kolor do przedmiotu, aby dodać atrybut color:

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

Starsza wersja po prostu zignoruje coloratrybut podczas otwierania dokumentu. Nowe wersje sprawdzają obecność coloratrybutu, a jeśli nie istnieje, przypisuje domyślny kolor.

Dzięki temu prostemu rozwiązaniu będziesz mieć zgodność zarówno wstecz, jak i do przodu.

użytkownik3123061
źródło
Niewielki problem z tą „prostą” opcją polega na tym, że usuwasz wszystkie nieoczekiwane atrybuty (lub nie zmieniasz ich) podczas zapisywania dokumentu. Jak wspomniano w innych odpowiedziach, lepsze rozwiązanie przynajmniej określa w pewnej agnostycznej wersji wersję, czy atrybut powinien zostać upuszczony, zachowany czy spowodować, że dokument stanie się tylko do odczytu dla wersji, które go nie rozumieją.
Mark Hurd
@Mark Hudr: Tak, po cichu zakładam, że zgodność wsteczna musi być wymagana, a zgodność dodatkowa to bonus. Kiedy ktoś otwiera nowy dokument w starej wersji aplikacji, nie powinien się dziwić, że zapisując go, stracił coś, co nie jest już widoczne w starej aplikacji. Wydaje mi się, że dodatkowa logika jest nadprodukowana.
user3123061,