头图

小失误,大问题 —— 为已发布的接口更名

写代码难免出现失误。在对某些已经发布的库进行升级或者审查的时候,就有可能会发现一些接口名称需要变更。比如,早期命名不符合特定规范,或者出现了难以发现的拼写错误等。有错当然是要改的,但是直接更名会影响到已发布的接口。粗暴的名称变更本质上是删除了旧接口,创建了新接口,对 API 用户来说极具破坏性 —— 用户会发现所有用到这些接口的地方都编译不过,或者不能运行了,这简直就是一场灾难。

本文主要以 C# 为例介绍对库接口更名的处理 —— 在 Assembly 内部,直接使用“重命名”重构方法,借助 IDE/Editor 的能力就可以完成变更;但是对于开放出去的接口,就必须保持接口兼容性,并声明过期时间。

本文以 C# 为例,但是处理方式和重构思想是语言无关的!

处理不符合规范的类属性命名

这一节主要是解决属性更名的问题。方法更名和属性更名是同样的道理,后面不再赘述。

假设有如下定义:

public class ActionResult : IActionResult {
    public int code { get; set; }
    public string message { get; set; }
}

这个定义命名不符合“公开属性使用 Pascal Case 命名规则”的规范。但作为一个已经被广泛使用的库,直接重命名将产生巨大的破坏,所以这里应该按正确的名称添加属性,并将旧的属性声明为过期。

public class ActionResult : IActionResult {
    public int Code { get; set; }
    public string Message { get; set; }
    
    [Obsolete("因规范命名,将从 v3.4 版本中删除,请使用 Code 代替")]
    public int code {
        get => Code;
        set => Code = value;
    }
    
    [Obsolete("因规范命名,将从 v3.4 版本中删除,请使用 Message 代替")]
    public string message {
        get => Message;
        set => Message = value;
    }
}

不过修改后的 ActionResult 会在序列化的时候出现问题。库中使用了 Newtonsoft JSON 来得到 JSON,按项目中的 JSON 规范,键名会使用 Camel Case 命名规则,所以在配置中添加了 CamelCasePropertyNamesContractResolver 实例。那么 ActionResultCodecode 属性都会被处理为 "code" 这个键名,于是发生了重名的冲突。

为了解决这个问题,可以为重名属性中的一个添加 Newtonsoft JSON 的 [JsonIgnore] 特性声明。为了保持兼容性,应该在新加的 CodeMessage 属性上添加这一声明,并在 Obsolete 描述和相关文档、发行说明中充分说明这一情况,告诉用户这一变更可能产生序列化问题。

也许你已经发现了,在这个问题上,我们作为库的发行方始终不能保持完全的兼容,毕竟用户使用什么样的序列化方式不是我们能决定的。也许用户会序列化成 XML,也许用户不使用 Newtonsoft JSON 而是使用别的 JSON 序列化工具,也许用户是需要遍历所有属性来实现某种逻辑 —— 作为库的发行方,我们除了通知和警告,无能为力 —— 这也是很多知名大库会把一些奇怪的接口保留很久的原因之一。

处理不符合规范的接口属性

注意到,上面示例的类实现了 IActionResult 接口。codemessage 的命名规范问题其实是从接口引入的,所以我们还需要处理接口属性的命名。处理接口属性会更为繁琐,但好在 C# 8.0 引入了接口默认实现的特性。假设之前 IActionResult 接口定义如下

public interface IActionResult {
    int code { get; set; }
    string message { get; set; }
}

使用默认实现添加 CodeMessage 并声明原来的属性即将失效

public interface IActionResult {
    int Code { get => code; set => code == value; }
    string Message { get => message; set => message = value; }

    [Obsolete("因规范命名,将从 v4.0 版本中删除,请改为实现 Code 属性")]
    int code { get; set; }

    [Obsolete("因规范命名,将从 v4.0 版本中删除,请改为实现 Message 属性")]
    string message { get; set; }
}

变更之后,前述 ActionResult 规范化前后的代码都可以通过编译。

这里特别需要注意的是,接口变更影响会比实现(类)的影响更大,应该给予用户足够的修改时间来处理,通常会在下个大版本或者越过几个大版本之后才实际删除声明为 Obsolete 的接口,进行破坏性的升级。

处理类名中的拼写错误

在代码审查的过程中,发现类 ApiErrorActioin 的名字中出现了拼写错误(追究为什么会让这样的错误出现在已发布的库中是有必要的,因为这通常是因为过程管理中存在漏洞,但不是本文的研究内容)。

直接更改类名同样是极具破坏性的,我们可以使用“重命名”重构方法更正名称,再添加一个与之存在继承关系,但保持原来错误名称的类来处理。

先使用重命名重构方法将 ApiErrorActioin 更正名称为 ApiErrorAction。现在所有 Assembly 内的错误名称都更正了。然后再定义一个继承自 ApiErrorActionApiErrorActioin(注意要实现与原来相同签名的所有构造函数重载),并添加废弃声明。

[Obsolete("因为拼写错误将从 v3.5 版本中删除,请使用正确命名的 ApiErrorAction")]
class ApiErrorActioin : ApiErrorAction {
    // 构造函数示例
    public ApiErrorActioin(string name): base(name) { }
}

这里需要特别注意的是,为保持兼容性而存在的 ApiErrorActioin 除了保持原来的非私有构造函数接口之外,其他接口都可以直接从修正后的 ApiErrorAction 继承而来。而且,这个要废弃的 ApiErrorActioin不应该包含任何逻辑代码

对接口更名也可以采用类似的方法。但要注意的是,这种处理方式添加了继承层次。虽然一般情况下不会造成用户的困扰,但是如果用户的“反射”代码有涉及到继承层次的逻辑,就有可能出现问题。因此这样的变更同样需要在文档中注明并发出警告。

小错误,大问题

对于人类来说,大脑的自动修正能力非常强(真正的人工智能!)所以一个小小的拼写错误或者大小字错误并不是什么大问题。但对计算机来说,只差一个字符,那就是完全不同的两个标识。更正拼写本身是个小事,但对于公共库接口的更名,可能会对用户产生巨大的影响,需要非常谨慎地处理。

然而,即使我们非常谨慎的处理了能想到的各种兼容性问题,差异仍然是不可避免的。你永远不知道用户会怎么使用这个库,所以也不知道用户会因为变更遇到什么奇怪的问题 —— 所以请重视文档和发行说明中的详细描述和警告。问题是藏不住的,公布它才是正确的选择。


边城客栈
全栈技术专栏,公众号「边城客栈」,[链接]

一路从后端走来,终于走在了前端!

56.1k 声望
26.5k 粉丝
0 条评论
推荐阅读
2022,二着二着又混过一年
收到思否小姐姐的活动提醒,才发觉又到了年底,该写“总结”了。说起总结,总有些倦——每天工作要写日报、项目上要写周报、月底要写月报、季度还有季总结,当然还有半年总结和年终总结……一年大约是 250 个工作日、50...

边城6阅读 739评论 2

封面图
剖析一下"抢茅台"脚本底层逻辑
2022年双十一大促已经完美收官,兄弟姐妹克服种种困难与挑战.. 备战的会议室忙碌中带着紧张,当峰值过后的喜悦不言而喻,今年备战室里听着对面的兄弟讲述了他抢茅台的经过,以及对马上来临的整点茅台活动期待,我...

京东云开发者1阅读 748

封面图
ASP.NET连接数据库(SQL Server)的操作
1.创建好项目后在我们的Web.config里面连接SQL Server数据库2.写入代码 {代码...} conStr是字段名字,后面连接需要,localhost是本地地址的意思,s是数据库中的表名。3.选中shujuku练习右击,选择添加,然后在选...

HealerHong3阅读 694评论 1

(持续更新,已更新至2022年11月26日)C语言经典题集合
(持续更新,最新时间2022年11月26日)1. 三个数由小到大排序输入任意3个整数,编程实现对这3个整数进行由小到大排序井将排序后的结果显示在屏幕上 {代码...} 2. a²+b²要求输入整数a和 b, 若a²+b²的结果大与100, ...

瞿小凯阅读 1.4k评论 2

封面图
javascript变量命名规范
程序员三大难题:变量命名、缓存失效、循环边界。驼峰命名首先,和其他语言一样,大部分变量建议采用驼峰命名法。 {代码...} 而对于常量,使用大写字母和下划线来组合命名。 {代码...} 根据变量类型来命名普通变...

一丁目1阅读 328

每刻和金蝶云星空接口打通对接实战
3000+中大型企业在用,新一代业财税一体化解决方案提供商。旗下拥有每刻报销、每刻档案、每刻云票、每刻财务共享云平台等,助力企业实现财务数字化转型。

大仲马小茶花阅读 1.1k

封面图
金蝶云星辰和旺店通·企业奇门单据接口集成
慧策(原旺店通)是一家技术驱动型智能零售服务商,基于云计算PaaS、SaaS模式,以一体化智能零售解决方案,帮助零售企业数字化智能化升级,实现企业规模化发展。

大仲马小茶花阅读 940

一路从后端走来,终于走在了前端!

56.1k 声望
26.5k 粉丝
宣传栏