我有一个类似于下面的情况。它是一个REST API helper类,具有针对不同API端点的方法,并为其提供头文件。其中一个标题,ChannelId,被硬编码为"Channel 1"。
从现在开始,应该可以有"频道2"代替。重构它的好策略是什么?我已经提供了我的建议,但不知何故,这感觉像是一种廉价的方法。因为我需要在很多方法中修改签名。
public class RestApiRequestHelper
{
public void MethodA()
{
var request = new RestRequest(RestRequest.GET);
AddHeaders(request);
//...
}
//... more similar methods using AddHeaders(request)
public void MethodZ()
{
var request = new RestRequest(RestRequest.GET);
AddHeaders(request);
//...
}
private void AddHeaders(RestRequest request)
{
request.AddHeader("ChannelId", "Channel 1");
//...
}
}
我的建议:
public class RestApiRequestHelper
{
public void MethodA(string channelId)
{
var request = new RestRequest(RestRequest.GET);
AddHeaders(request, channelId);
//...
}
//... more similar methods using AddHeaders(request)
public void MethodZ(string channelId)
{
var request = new RestRequest(RestRequest.GET);
AddHeaders(request, channelId);
//...
}
private void AddHeaders(RestRequest request, string channelId)
{
request.AddHeader("ChannelId", channelId);
//...
}
}
从API消费者的角度来看,新版本是一个突破性的变化,它真的很容易出错。
- 突破性更改:消费者必须采用新的API,因为它不向后兼容。
- 易出错:消费者现在可以用几乎任何东西调用这个API,比如:
null
,"ThisIsNotExistingChannelId"
,"ThisIsAMisspelledChannelId"
,await LongRunningOperationToObtainChannelId()
等。
的向后兼容性如果你不想引入破坏性的更改(这样你的客户端就可以使用API而不需要任何代码更改),那么你可以使用可选参数。
默认值
参数public void MethodA(string channelId = "Channel 1")
{
var request = new RestRequest(RestRequest.GET);
AddHeaders(request, channelId);
//...
}
带回退值的参数
public void MethodA(string channelId = default)
{
var request = new RestRequest(RestRequest.GET);
AddHeaders(request, channelId ?? "Channel 1");
//...
}
参数限制如果你不允许任何类型的通道标识符,那么你也可以限制它。
运行时检查private static readonly ImmutableArray<string>
ValidChannelIds = ImmutableArray.Create("Channel 1", "Channel 2");
public void MethodA(string channelId = default)
{
if(!ValidChannelIds.Contains(channelId))
throw new ArgumentOutOfRangeException(nameof(channelId),
$"The provided channel is invalid. Valid ids are: '{string.Join(',', ValidChannelIds)}'");
...
}
编译时间和运行时检查
public enum Channels
{
[Description("Channel 1")]
Ch1 = 0,
[Description("Channel 2")]
Ch2 = 2,
}
public void MethodA(Channels channelId = Channels.Ch1)
{
if(!Enum.IsDefined(typeof(Channels), channelId))
throw new ArgumentOutOfRangeException(nameof(channelId),
$"The provided channel is invalid. Valid ids: '{string.Join(',', Enum.GetNames(typeof(Channels)))}'");
var fieldInfo = typeof(Channels).GetField(channelId.ToString("G"));
var chId = fieldInfo.GetCustomAttribute<DescriptionAttribute>().Description;
...
}
需要Enum.IsDefined
,因为您也可以这样调用方法:MethodA((Channels)3);
和3不会有Description
。