Qual é um bom design para permitir a compatibilidade com versões anteriores de arquivos entre diferentes versões de software?

14

Qual é um bom design para permitir a compatibilidade com versões anteriores de um tipo de arquivo entre diferentes versões de software?

Por exemplo, como a Microsoft obtém o Word 2007, 2010 e 2013 etc ... para todos os arquivos docx abertos, mas edições diferentes podem salvar mais / menos dados e salvá-los de maneiras ligeiramente diferentes, tudo para o mesmo tipo de arquivo e um o arquivo salvo em uma versão pode ser aberto em outra, mas certos elementos do arquivo podem não estar disponíveis em versões mais antigas?

Quero dizer, a maneira realmente óbvia de fazer isso é ter algo como

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

mas isso parece incrivelmente monolítico, não muito extensível e provavelmente leva a muitos códigos copiados / colados.

Então, eu estava pensando em usar uma interface de base para todas as versões que definem as estruturas imutáveis, como o cabeçalho, que precisam estar presentes no arquivo e os métodos que precisam estar disponíveis para serialização / desserialização e, em seguida, herança múltipla para que cada A classe da nova versão que implementa a interface herda a versão antiga e substitui apenas as coisas que foram alteradas, já que o arquivo será o mesmo, na maior parte.

Não estou realmente preocupado com a estrutura do arquivo, pois já está decidido que usaremos XML, e o esquema inicial já está, em geral, decidido. No entanto, sem dúvida, haverá alterações no futuro, e eu só quero poder projetar o código de uma maneira que facilite a acomodação dessas alterações.

JJBurgess
fonte
6
Você deve projetar o formato do arquivo para que não apenas ignore as informações que estão faltando porque a fonte é de uma versão anterior, mas também as informações que ele não espera porque a fonte é de uma versão mais recente. Se você está começando do zero, por favor, faça compatibilidade com a frente também. Quase não é um esforço extra e dobra a utilidade do seu software.
Kilian Foth
Em aberto, você sempre saberá com antecedência (por exemplo, do cabeçalho) com qual versão de arquivo está lidando? Além disso, para fazer outra solicitação, verifique se há arquivos corrompidos ou maliciosos e não deixe que eles causem problemas. Seus administradores de sistema vão agradecer :).
Cxw
1
Sim, o número da versão sempre estará no cabeçalho do arquivo e o formato do cabeçalho nunca será alterado. Vamos continuar com a ideia de que os arquivos criados entre pequenas revisões de software devem ser compatíveis, ou seja, um arquivo criado na v1.1 pode ser aberto na v1.2 e vice-versa, embora algumas funcionalidades do 1.2 possam estar ausentes na 1.1, mas as principais revisões quebrará a compatibilidade anterior; portanto, o material escrito na v2 não será aberto na v1, mas o material escrito na v1 será aberto na v2.
JJBurgess 03/07
E quanto à corrupção, os arquivos contêm DSL e o programa que os abre / fecha é um IDE / compilador interno personalizado. Eles não chegarão nem perto de um ambiente de produção; portanto, o administrador não precisa se preocupar.
JJBurgess

Respostas:

10

Você pode dar uma olhada no formato de arquivo PNG e como ele lida com a compatibilidade de versão. Cada bloco possui um ID que descreve o tipo de bloco e possui alguns sinalizadores que informam ao software o que fazer se não conseguir entender esse ID. Por exemplo "você não pode ler o arquivo se não entender este bloco" ou "você pode ler o arquivo, mas não pode modificá-lo" ou "você pode modificar o arquivo, mas precisa excluir este bloco". Para compatibilidade com versões anteriores, seu software só precisa lidar com a situação quando nenhum dado esperado estiver presente.

gnasher729
fonte
Boa ideia! O formato PNG depende de recursos e não de versões. Isso significa, no entanto, que o formato básico nunca deve mudar. (ou seja, o cabeçalho que define a característica.)
Florian Margaine
Isso é interessante. Estou lendo a especificação do arquivo no momento. Eu gosto da idéia de pedaços críticos e auxiliares, e pode tentar trabalhar isso no.
JJBurgess
3

Uma maneira de fazer isso pode ser usando uma classe base e uma interface com as funções básicas para o tratamento de arquivos. Em seguida, use classes para cada versão que se estende da classe base para lidar com todos os casos específicos da versão. As funções que podem mudar podem ser virtuais na classe de resumo base, se houver apenas implementações específicas da versão. Quando você precisar de uma classe para manipular o arquivo, use uma fábrica que obtenha a implementação específica da versão da interface de manipulação de arquivos.

par
fonte
Meu único problema é que você acabaria duplicando a implementação específica da versão para cada revisão subsequente. Digamos que você tenha três métodos de classe base: ReadNames (), ReadAges () e ReadAddresses () e na V2 da classe, faça uma alteração em ReadAges (). Se na V3, você decide alterar o ReadNames (), se todas as classes específicas da sua versão forem herdadas da base, você perderá as alterações na V2 ou precisará copiar / colar as alterações na V2 na implementação da V3 também.
JJBurgess 03/07
1
A implementação de leituras pode chamar uma classe diferente que contém a implementação real de como ler as idades para esta versão. Fazer sua classe será mais configuração de interfaces / fábricas do que programação real.
pares
2

Eu fiz isso com XML e funciona bem:

Simplesmente permita que qualquer elemento do seu documento tenha atributos e subelementos (e quando a ordem não for importante - em qualquer ordem). A partir da primeira versão do programa - ao ler o documento, ignore os atributos e subelementos que você não conhece na versão atual.

No futuro, quando você estiver adicionando um novo recurso à nova versão do programa, adicione atributo ou subelemento. As versões mais antigas irão ignorá-lo. A nova versão deve verificar a pressão do atributo ou subelemento e lidar com isso.

Por exemplo, você tem alguns itens com textos:

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

E na versão mais recente, você gostaria de adicionar cor ao item para adicionar o atributo color:

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

A versão mais antiga simplesmente ignorará o coloratributo ao abrir o documento. Novas versões verificam a pressão do coloratributo e, se não existe, atribuem a cor padrão.

Com esta solução simples, você terá compatibilidade com versões anteriores e posteriores.

user3123061
fonte
O pequeno problema com isso como uma opção "simples" é que você removerá todos os atributos inesperados (ou os manterá inalterados) ao salvar o documento. Conforme mencionado em outras respostas, uma solução melhor determina pelo menos de alguma forma independente de versão, se um atributo deve ser descartado, mantido ou fazer com que o documento se torne somente leitura para as versões que não o compreendem.
Mark-Hurd
Mark Hudr: Sim, estou assumindo silenciosamente que a compatibilidade com versões anteriores é obrigatória e a compatibilidade com versões anteriores é um bônus. Quando alguém abre um novo documento na versão antiga dos aplicativos, não deve se surpreender ao salvá-lo, perdendo algo que ainda não é visível no aplicativo antigo. Lógicas adicionais me parecem exageradas.
user3123061