Errors are inevitable when writing code. When upgrading or reviewing certain released libraries, it may be found that some interface names need to be changed. For example, early naming did not conform to specific specifications, or there were spelling errors that were difficult to detect. If there is an error, of course, it needs to be changed, but the direct name change will affect the published interface. The crude name change essentially deletes the old interface and creates a new interface, which is extremely destructive for API users-users will find that all the places where these interfaces are used are not compiled or can not be run, which is simply A disaster.
This article mainly uses C# as an example to introduce the processing of renaming the library interface-inside the Assembly, directly use the "rename" reconstruction method, with the help of IDE/Editor capabilities to complete the change; but for the open interface, it must be maintained Interface compatibility, and declare the expiration time.
This article takes C# as an example, but the processing method and refactoring ideas are language-independent!
Handling non-compliant class attribute naming
This section is mainly to solve the problem of attribute renaming. Method renaming is the same as attribute renaming, so I won’t repeat it later.
Assuming the following definition:
public class ActionResult : IActionResult {
public int code { get; set; }
public string message { get; set; }
}
The naming of this definition does not conform to the specification of "Public Attributes Use Pascal Case Naming Rules". But as a library that has been widely used, renaming directly will cause huge damage, so here you should add attributes with the correct names and declare the old attributes as expired.
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;
}
}
However, the modified ActionResult
will have problems during serialization. Newtonsoft JSON is used in the library to get JSON. According to the JSON specification in the project, the key name will use the Camel Case naming rules, so the CamelCasePropertyNamesContractResolver
instance is added to the configuration. So ActionResult
of Code
and code
property will be treated as "code"
the key name, then clashed same name.
To solve this problem, you can add Newtonsoft JSON's [JsonIgnore]
feature declaration for one of the attributes with the same name. In order to maintain compatibility, this statement should be added to the newly added Code
and Message
attributes, and Obsolete
description and related documents and release notes to inform users that this change may cause serialization problems.
Perhaps you have discovered that on this issue, we, as the publisher of the library, have not been able to maintain complete compatibility. After all, it is not up to us to decide what serialization method users use. Maybe users will serialize into XML, maybe users don’t use Newtonsoft JSON but use other JSON serialization tools, maybe users need to traverse all attributes to implement certain logic-as the publisher of the library, we can do nothing except notice and warning —— This is also one of the reasons why many well-known big libraries keep some strange interfaces for a long time.
Handling non-compliant interface attributes
Note that the class in the example above implements the IActionResult
interface. code
and message
are actually introduced from the interface, so we also need to deal with the naming of interface attributes. Processing interface attributes will be more cumbersome, but fortunately, C# 8.0 introduces the characteristics of the default implementation of the interface. Assuming that the previous IActionResult
interface is defined as follows
public interface IActionResult {
int code { get; set; }
string message { get; set; }
}
Use the default implementation to add Code
and Message
and declare that the original properties are about to expire
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; }
}
After the change, the code before and after the normalization of ActionResult
It is particularly important to note here that the impact of interface changes will be greater than the impact of implementation (class). should give users enough modification time . Usually, the statement is actually deleted after the next major version or after several major versions. Destructive upgrade for Obsolete's interface.
Handle misspellings in class names
During the code review process, it was found ApiErrorActioin
(it is necessary to investigate why such an error appeared in the published library, because this is usually because of a loophole in the process management, but not The research content of this article).
Changing the class name directly is also extremely destructive. We can use the "rename" reconstruction method to correct the name, and then add a class that has an inheritance relationship with it, but keeps the original wrong name.
First use rename refactoring methods ApiErrorActioin
correct name ApiErrorAction
. Now all incorrect names in the Assembly have been corrected. And then define a class that inherits from ApiErrorAction
of ApiErrorActioin
(note that to achieve the same with all the original signature of the constructor overloads), and discard the statement added.
[Obsolete("因为拼写错误将从 v3.5 版本中删除,请使用正确命名的 ApiErrorAction")]
class ApiErrorActioin : ApiErrorAction {
// 构造函数示例
public ApiErrorActioin(string name): base(name) { }
}
What needs special attention here is that in order to maintain compatibility, ApiErrorActioin
maintains the original non-private constructor interface, and other interfaces can be directly inherited ApiErrorAction
ApiErrorActioin
in to be discarded should not contain any logic code .
A similar method can be used to rename the interface. But it should be noted that this processing method adds an inheritance hierarchy. Although under normal circumstances it will not cause user trouble, but if the user's "reflection" code involves inheritance hierarchy logic, there may be problems. Therefore, such changes also need to be noted in the document and issued a warning.
Small mistake, big problem
For humans, the brain's automatic correction ability is very strong (true artificial intelligence!), so a small spelling error or mistake in size is not a big problem. But for the computer, there is only one character difference, that is, two completely different logos. Correcting the spelling itself is a trivial matter, but the rename of the public library interface may have a huge impact on users and needs to be handled very carefully.
However, even if we deal with all the compatibility issues we can think of very carefully, the difference is still inevitable. You never know how users will use this library, so do not know what strange problems users will encounter due to changes-so please pay attention to the detailed descriptions and warnings in the documentation and release notes. The problem cannot be hidden, and publishing it is the right choice.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。