I have a situation that looks similar to below. It's a REST API helper class that has methods for different API endpoints and provides headers to it. One header, ChannelId, has been hard coded as "Channel 1".
From now on, it should be possible to have "Channel 2" instead. What is a good strategy to Refactor this? I have provided my suggestion but it feels like a cheap way to go somehow. since I need to change the signature in tons of methods.
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");
//...
}
}
My suggestion:
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);
//...
}
}
From the API consumer perspective the new version is a breaking change and it is really error-prone.
null
, "ThisIsNotExistingChannelId"
, "ThisIsAMisspelledChannelId"
, await LongRunningOperationToObtainChannelId()
, etc.If you don't want to introduce breaking change (so your clients can use the API without any code change) then you can use optional parameters.
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");
//...
}
If you don't want to allow any kind of channel identifier then you can restrict that as well.
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;
...
}
The Enum.IsDefined
is needed because you can call the method like this as well: MethodA((Channels)3);
and 3 won't have Description
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.