¿Cuál es un buen diseño para permitir la compatibilidad con versiones anteriores de archivos entre diferentes versiones de software?

14

¿Cuál es un buen diseño para permitir la compatibilidad con versiones anteriores de un tipo de archivo entre diferentes versiones de software?

Por ejemplo, ¿cómo obtiene Microsoft Word 2007, 2010 y 2013, etc.? Para todos los archivos docx abiertos, pero diferentes ediciones pueden guardar más / menos datos y guardar los datos de formas ligeramente diferentes, todo en el mismo tipo de archivo y un El archivo que se guarda en una versión se puede abrir en otra, pero ¿ciertos elementos del archivo pueden no estar disponibles en versiones anteriores?

Quiero decir, la forma realmente obvia de hacerlo es tener 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
            .....
}

pero eso parece increíblemente monolítico, no muy extensible y es probable que genere una gran cantidad de código copiado / pegado.

Así que estaba pensando en usar una interfaz base para todas las versiones que define las estructuras inmutables, como el encabezado, que deben estar presentes en el archivo, y los métodos que deben estar disponibles para la serialización / deserialización, y luego la herencia múltiple para que cada La clase de la nueva versión que implementa la interfaz hereda la versión anterior, y solo anula las cosas que han cambiado, ya que el archivo será el mismo, en su mayor parte.

Realmente no me preocupa la estructura del archivo, ya que ya se decidió que usaremos XML, y el esquema inicial ya está decidido. Sin embargo, sin duda habrá cambios en él en el futuro, y solo quiero poder diseñar el código de una manera que facilite acomodar estos cambios.

JJBurgess
fuente
66
Debe diseñar el formato de archivo de modo que no solo ignore la información que falta porque la fuente es de una versión anterior, sino también la información que no espera porque la fuente es de una versión más nueva . Si está comenzando desde cero, por favor, haga también la compatibilidad de reenvío . Casi no supone un esfuerzo adicional y duplica la utilidad de su software.
Kilian Foth
En un abierto, ¿sabrá siempre por adelantado (por ejemplo, desde el encabezado) con qué versión de archivo está tratando? Además, para realizar otra solicitud, compruebe si hay archivos corruptos o maliciosos y no permita que causen problemas. Tus administradores de sistemas te lo agradecerán :).
cxw
1
Sí, el número de versión siempre estará en el encabezado del archivo y el formato del encabezado nunca cambiará. Vamos con la idea de que los archivos creados entre revisiones menores de software deberían ser compatibles, es decir, un archivo creado en v1.1 se puede abrir en v1.2 y viceversa, aunque algunas funciones de 1.2 pueden faltar en 1.1 pero las revisiones principales romperá la compatibilidad hacia adelante, por lo que las cosas escritas en v2 no se abrirán en v1, pero las cosas escritas en v1 se abrirán en v2.
JJBurgess
Y en cuanto a la corrupción, los archivos contienen DSL, y el programa que los abre / cierra es un compilador / IDE interno personalizado. Estos no se acercarán a un entorno de producción, por lo que el administrador no debe preocuparse.
JJBurgess

Respuestas:

10

Puede echar un vistazo al formato de archivo PNG y cómo maneja la compatibilidad de la versión. Cada bloque tiene una identificación que describe qué tipo de bloque es, y tiene algunos indicadores que le dicen al software qué hacer si no puede entender esa identificación. Por ejemplo, "no puede leer el archivo si no comprende este bloque", o "puede leer el archivo pero no modificarlo", o "puede modificar el archivo pero debe eliminar este bloque". Para la compatibilidad con versiones anteriores, su software solo necesita manejar la situación cuando los datos esperados no están presentes.

gnasher729
fuente
¡Gran idea! El formato PNG se basa en características y no en versiones. Sin embargo, sí significa que el formato básico nunca debe cambiar. (es decir, el encabezado que define la función).
Florian Margaine
Eso es interesante. Estoy leyendo la especificación del archivo en este momento. Me gusta la idea de fragmentos críticos y auxiliares, y podría tratar de trabajar en esto.
JJBurgess
3

Una forma de hacerlo puede ser mediante el uso de una clase base y una interfaz con las funciones básicas para el manejo de sus archivos. Luego use clases para cada versión que se extiendan desde la clase base para manejar todos los casos específicos de la versión. Las funciones que pueden cambiar pueden ser virtuales en su clase base de resumen si solo hay implementaciones específicas de la versión. Cuando necesite una clase para manejar el archivo, use una fábrica que obtenga la implementación específica de la versión de la interfaz de manejo de archivos.

mirar
fuente
Mi único problema con esto es que terminaría duplicando la implementación específica de la versión para cada revisión posterior. Digamos que tiene tres métodos de clase base: ReadNames (), ReadAges () y ReadAddresses () y en V2 de la clase, realiza un cambio en ReadAges (). Si en V3, decide realizar un cambio en ReadNames (), si todas las clases específicas de su versión heredan de la base, perderá sus cambios de V2 o necesitará copiar / pegar los cambios de V2 en la implementación de V3 también.
JJBurgess
1
La implementación de las lecturas puede llamar a una clase diferente que tiene la implementación real sobre cómo leer las edades para esta versión. Hacer su clase será más configuración de interfaces / fábricas que la programación real.
igual
2

He hecho esto con XML y funciona bien:

Simplemente permita que cualquier elemento en su documento tenga atributos y subelementos (y cuando el orden no es importante, en cualquier orden). A partir de la primera versión del programa: al leer el documento, ignore los atributos y subelementos que no conoce en la versión actual.

En el futuro, cuando agregue una nueva función a una nueva versión del programa, agregue un atributo o subelemento. Las versiones anteriores lo ignorarán. La nueva versión debe verificar la presencia del atributo o subelemento y manejarlo.

Por ejemplo, tiene algunos elementos con textos:

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

Y en la versión más reciente, le gustaría agregar color al elemento para que agregue el atributo color:

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

La versión anterior simplemente ignorará el coloratributo al abrir el documento. Las nuevas versiones verifican la presencia decolor atributo y, si no existe, asignan el color predeterminado.

Con esta solución simple, tendrá compatibilidad con versiones anteriores y posteriores.

usuario3123061
fuente
El pequeño problema con esto como una opción "simple" es que eliminará todos los atributos inesperados (o los mantendrá sin cambios) al guardar el documento. Como se mencionó en otras respuestas, una mejor solución al menos determina de alguna manera independiente de la versión si un atributo debe descartarse, guardarse o hacer que el documento sea de solo lectura para las versiones que no lo entienden.
Mark Hurd
@Mark Hudr: Sí, estoy asumiendo silenciosamente que la compatibilidad con versiones anteriores es obligatoria y que la compatibilidad con versiones posteriores es una ventaja. Cuando alguien abre un documento nuevo en la versión anterior de las aplicaciones, no debería sorprenderse de que cuando lo guarde, haya perdido algo que ya no es visible en la aplicación anterior. Lógicas adicionales me parecen de ingeniería excesiva.
user3123061