Language agnostic 存储不同版本数据的首选方式是什么?

Language agnostic 存储不同版本数据的首选方式是什么?,language-agnostic,Language Agnostic,当您编写的应用程序需要以相同的方式读取和处理两个版本的数据时,构造类以表示该数据的最佳方法是什么。我提出了三种情况: 共同基础/特定子项 数据联合 独特结构 版本1汽车示例 byte DoorCount int Color byte HasMoonroof byte HasSpoiler float EngineSize byte CylinderCount 第二版汽车 byte DoorCount int Color enum:int MoonRoofType enum:int TrunkA

当您编写的应用程序需要以相同的方式读取和处理两个版本的数据时,构造类以表示该数据的最佳方法是什么。我提出了三种情况:

  • 共同基础/特定子项
  • 数据联合
  • 独特结构
  • 版本1汽车示例

    byte DoorCount
    int Color
    byte HasMoonroof
    byte HasSpoiler
    float EngineSize
    byte CylinderCount
    
    第二版汽车

    byte DoorCount
    int Color
    enum:int MoonRoofType
    enum:int TrunkAccessories
    enum:int EngineType
    
    共同基础/特定子项

    使用此方法,两个版本的数据之间有一个公共字段基类,每个版本的数据都有一个子类

    class Car {
        byte DoorCount;
        int Color;
    }
    
    class CarVersion1 : Car {
        byte HasMoonroof;
        byte HasSpoiler;
        float EngineSize;
        byte CylinderCount;
    }
    
    class CarVersion2 : Car {
        int MoonRoofType;
        int TrunkAccessories;
        int EngineType;
    }
    
    class Car {
        CarVersion version;
        byte DoorCount;
        int Color;
        int MoonRoofType;     //boolean if Version 1
        int TrunkAccessories; //boolean if Version 1
        int EngineType;       //CylinderCount if Version 1
        float EngineSize;     //Not used if Version2
    }
    
    优势

    • 面向对象范式
    弱点

    • 如果发布了删除公共字段的新版本,现有子类将不得不更改
    • 一个概念单元的数据在两个定义之间分割,不是因为任何对其本身有意义的分割
    数据联合

    这里,Car被定义为所有版本数据中Car字段的并集

    class Car {
        byte DoorCount;
        int Color;
    }
    
    class CarVersion1 : Car {
        byte HasMoonroof;
        byte HasSpoiler;
        float EngineSize;
        byte CylinderCount;
    }
    
    class CarVersion2 : Car {
        int MoonRoofType;
        int TrunkAccessories;
        int EngineType;
    }
    
    class Car {
        CarVersion version;
        byte DoorCount;
        int Color;
        int MoonRoofType;     //boolean if Version 1
        int TrunkAccessories; //boolean if Version 1
        int EngineType;       //CylinderCount if Version 1
        float EngineSize;     //Not used if Version2
    }
    
    优势

    • 嗯。。。一切都在一个地方
    弱点

    • 强制大小写驱动代码
    • 当发布另一个版本或删除旧版本时,很难维护
    • 难以概念化。字段的含义根据版本而更改
    独特的结构

    这里的结构彼此之间没有OOP关系。但是,如果/当代码希望以相同的方式处理接口时,两个类都可以实现接口

    class CarVersion1 {
        byte DoorCount;
        int Color;
        byte HasMoonroof;
        byte HasSpoiler;
        float EngineSize;
        byte CylinderCount;
    }
    
    class CarVersion2 {
        byte DoorCount;
        int Color;
        int MoonRoofType;
        int TrunkAccessories;
        int EngineType;
    }
    
    优势

    • 直截了当的方法
    • 如果添加新版本或删除旧版本,则易于维护
    弱点

    • 这是一种反模式

    有没有比这更好的方法?很明显,我倾向于最后一种方法,但第一种方法更好吗?

    为什么第三种选择,每个版本的不同结构,是一个坏主意还是反模式

    如果在一个公共应用程序/模块中使用两个版本的数据结构,则它们必须实现相同的接口。时期编写两个不同的应用程序模块来处理两个不同版本的数据结构肯定是站不住脚的。底层数据模型差异极大这一事实应该是无关紧要的。毕竟,编写对象的目标是实现实用级别的封装

    当您继续以这种方式编写代码时,您应该最终找到两个类中的代码相似或冗余的地方。如果将这些公共代码从各种版本类中移出,最终可能会得到版本类,这些版本类不仅实现相同的接口,而且还可以实现相同的基类/抽象类。瞧,你找到了你的“第一”选择

    我认为这是在数据不断变化的环境中的最佳途径。它需要对较旧的代码进行一些研究和“回头看”,但值得享受代码清晰性和可重用组件带来的好处


    另一个想法:在您的示例中,基类是“Car”。在我看来,基类与它的继承者如此“接近”几乎是不可能的。更现实的基类或接口集可能是“可版本化”、“可升级”、“OptionContainer”等。根据我的经验,YMMV。

    使用第二种方法,并通过接口对其进行增强。请记住,您可以实现多个接口“版本”,这给了您向后兼容的能力!我希望你能明白我的意思;)

    执行以下要求:

    需要以相同方式读取和处理两个版本数据的应用程序

    我想说的是,最重要的是,通过数据抽象层将所有逻辑汇集起来,这样您的逻辑就不必关心您使用的是数据的版本1、版本2还是版本n

    class Car {
        byte DoorCount;
        int Color;
    }
    
    class CarVersion1 : Car {
        byte HasMoonroof;
        byte HasSpoiler;
        float EngineSize;
        byte CylinderCount;
    }
    
    class CarVersion2 : Car {
        int MoonRoofType;
        int TrunkAccessories;
        int EngineType;
    }
    
    class Car {
        CarVersion version;
        byte DoorCount;
        int Color;
        int MoonRoofType;     //boolean if Version 1
        int TrunkAccessories; //boolean if Version 1
        int EngineType;       //CylinderCount if Version 1
        float EngineSize;     //Not used if Version2
    }
    
    实现这一点的一种方法是只使用一个数据类,即数据的最“增强”版本。基本上,它会有
    moonroottype
    ,但不会有
    HasMoonRoof
    ,因为这是可以推断出来的。这个类也不应该有任何过时的属性,因为应该由数据抽象层决定默认值是什么

    最后,您将拥有一个完全不关心数据版本的应用程序


    至于数据抽象层,您可能希望也可能不希望每个版本都有数据类。最有可能的是,您只需要为数据结构的每个版本提供一个类,使用
    Save
    Load
    方法来存储/创建应用程序逻辑使用的数据实例。

    好吧,也许我说了最后一个选项是反模式,因为它不使用基类来表示“相同”的两个表示概念对象。“很难证明基类与它的继承者如此接近”-是的,我只是想设计一个简短的示例来演示方法。所以我很抱歉,我知道你会提倡第三种选择,如果在编写应用程序代码时需要的话,这会演变为第一种选择?是的,这是我(长)答案的一个令人尴尬的简洁摘要。