简体   繁体   中英

Refactoring one hard-coded string, used in numerous methods, into two

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.

  • Breaking change: The consumer have to adopt to the new API, because that is not backward compatible.
  • Error-prone: The consumer now can call this API with almost anything, like: null , "ThisIsNotExistingChannelId" , "ThisIsAMisspelledChannelId" , await LongRunningOperationToObtainChannelId() , etc.

Backward compatibility

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.

Parameter with default value

public void MethodA(string channelId = "Channel 1")
{
   var request = new RestRequest(RestRequest.GET);
   AddHeaders(request, channelId);
   //...
}

Parameter with fallback value

public void MethodA(string channelId = default)
{
   var request = new RestRequest(RestRequest.GET);
   AddHeaders(request, channelId ?? "Channel 1");
   //...
}

Parameter restrictions

If you don't want to allow any kind of channel identifier then you can restrict that as well.

Runtime check

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)}'");
    ...
}

Compile time and Runtime check

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM