使库向后兼容的最佳实践?

Best practices to make a library backwards compatible?

我有一个实用程序库 classes。 classes 之一执行类似于 tar 的文件归档,因为 .NET 已经有 gz

存档class使用magic numbers进行版本控制,因此它可以支持使用该库的旧版本压缩的文件。这是一个例子:

public void ExtractTo(DirectoryInfo directory, bool overwrite = false)
{
    // removed sanity checks, try/catch, etc. for brevity

    using (FileStream archiveStream = 
        new FileStream(this.archivefile.FullName, FileMode.Open))
    {
        byte[] magicNumber = new byte[8];
        archiveStream.Read(magicNumber, 0, 8);

        string ArchiveLib_Version = Encoding.UTF8.GetString(magicNumber);

        switch (ArchiveLib_Version)
        {
            case "ALib0000": 
                ExtractTo_v0(archiveStream, dirInfo);
                break;
            case "ALib0001":
                ExtractTo_v1(archiveStream, dirInfo);
                break;
            default:
                throw new IOException("Archive file invalid.");
        }
    }
}

但是,版本化方法依赖于库中的其他例程,这些例程将来可能会发生变化,例如我的 Stream.SubStream 扩展方法:

protected void ExtractTo_v1(FileStream archiveStream, DirectoryInfo dirInfo)
{
    // ...

    foreach (ArchiveFileInfo arcInfo in archiveHeader)
    {
        Stream sourceBytes = archiveStream.SubStream(
            arcInfo.archivePosition, arcInfo.archiveLength);

        using (GZipStream gz = new GZipStream(sourceBytes, 
            CompressionMode.Decompress, true))
        {
            // ...
        }
    }
}

我是否需要对整个库进行版本控制、创建依赖方法列表、做其他事情?

在库中保持向后兼容性的最佳做法是什么?

您有两个广泛的选择:

在保持向后兼容性的同时修改现有代码

您可以继续修改 SubStream(和其他相关方法)的实现,以用于您的存档的较新版本。这种方法有助于最大限度地减少代码库中重复代码的数量,帮助您遵守 DRY 原则。

Unit tests can be used to ensure that SubStream (and other dependent methods) continue to work for old archive versions. You could write a set of tests that call SubStream with a mock 提供旧存档数据的流,并确保它为您提供预期的结果。当您更改新存档版本的实现 SubStream 时,一组测试可以让您确信您没有为旧存档版本破坏它。

缺点是您必须花精力维护一套测试。

为新版本编写新代码并保留旧代码

此方法仿照 Open/Closed principle。如果您的 SubStream 方法不允许修改,那么它永远不会中断。如果您需要针对新存档版本的 SubStream 方法的新功能或不同功能,您可以简单地为新格式编写一个新的 SubStream 方法,并继续使用旧的、未受影响的 SubStream旧格式的方法。

这可能比上述选项省力得多 - 无需进行全面的回归测试,只需继续为您的新功能编写新代码并收工即可。缺点是您最终可能会在代码库中得到重复的代码。如果您发现一个缺陷会影响您的方法的多个版本,这可能会成为一个问题 - 您将必须在每个版本中找到并修复错误,而不是仅仅在一个地方修复它。

采用哪种方法完全取决于您:)