3
\$\begingroup\$

I need to return a "human readable" description based on a minimum and a maximum value. Note that the minimum is included in the condition, but the maximum is excluded -- so for instance if the parameters are 6 and 9, the valid values are 6, 7 and 8, but not 9. The maximum value is also nullable.

I started out with nested switch statements, but I quickly abandoned them because they were unreadable and would be hard to maintain. Instead I kept the original switch, but moved each nested switch to its own method.

Note that the code currently supports only a handful of cases, but these are the ones currently needed. Though it is possible others will need to be added in the future and thus I'd like to have something that would be easy to maintain.

The code as featured below doesn't feel "mature" to me, but I don't see another solution right now. Perhaps the Strategy Pattern could be a solution, but it feels a bit overkill for this purpose.

internal class InterpretationRetriever
{
    public static string Execute(int minimum, int? maximum)
    {
        switch (minimum)
        {
            case 0:
                return MinimumIsZero(maximum);
            case 1:
                return MinimumIsOne(maximum);
            case 3:
                return MinimumIsThree(maximum);
            case 5:
                return MinimumIsFive(maximum);
            case 6:
                return MinimumIsSix(maximum);
            case 9:
                return MinimumIsNine(maximum);
            case 10:
                return MinimumIsTen(maximum);
            default:
                return null;
        }
    }

    private static string MinimumIsZero(int? maximum)
    {
        if (!maximum.HasValue)
        {
            return "Zero or more";
        }

        switch (maximum)
        {
            case 1:
                return "Less than one";
            case 3:
                return "Less than three";
            case 5:
                return "Less than five";
            default:
                return null;
        }
    }

    private static string MinimumIsOne(int? maximum)
    {
        if (!maximum.HasValue)
        {
            return "One or more";
        }
        return null;
    }

    private static string MinimumIsThree(int? maximum)
    {
        if (!maximum.HasValue) 
            return null;

        switch (maximum)
        {
            case 6:
                return "From three to five";
            default:
                return null;
        }
    }

    private static string MinimumIsFive(int? maximum)
    {
        if (!maximum.HasValue) 
            return null;

        switch (maximum)
        {
            case 10:
                return "From five to nine";
            default:
                return null;
        }
    }

    private static string MinimumIsSix(int? maximum)
    {
        if (!maximum.HasValue) 
            return null;

        switch (maximum)
        {
            case 9:
                return "From three to eight";
            default:
                return null;
        }
    }

    private static string MinimumIsNine(int? maximum)
    {
        if (!maximum.HasValue)
        {
            return "Nine or more";
        }
        return null;
    }

    private static string MinimumIsTen(int? maximum)
    {
        if (!maximum.HasValue)
        {
            return "Ten or more";
        }
        return null;
    }
}
\$\endgroup\$
1
  • \$\begingroup\$ D'oh, just noticed a bug! In MinimumIsSix(int? maximum) the case 9 should return "From six to eight". \$\endgroup\$ – BCdotWEB May 20 '15 at 13:53
5
\$\begingroup\$

Perhaps instead of this convoluted branching, you could simply handle the three cases: whether there is a maximum and a non-zero minimum, a maximum and a minimum of 0 or there is no maximum.

private static Dictionary<int, string> humanReadableNumbers = new Dictionary<int,string>()
    {
        {0, "zero"},
        {1, "one"},
        {2, "two"},
        //etc...
    };

public static string Execute(int minimum)
{
    return string.Format("{0} or more", CultureInfo.CurrentCulture.TextInfo.ToTitleCase(humanReadableNumbers[minimum]));
}

public static string Execute(int minimum, int maximum)
{
    return minimum == 0 ? string.Format("Less than {0}", humanReadableNumbers[maximum]) : string.Format("From {0} to {1}", humanReadableNumbers[minimum], humanReadableNumbers[maximum-1]);
}

With this method, adding extra numbers is very simple. I've additionally chosen to split the method into two methods, one for no maximum, and one for a maximum. I feel it shows the different use-cases better, but if it is too much work to refactor all uses of it, this will work instead:

public static string Execute(int minimum, int? maximum)
{
    if(maximum.HasValue())
    { 
        return string.Format("{0} or more", CultureInfo.CurrentCulture.TextInfo.ToTitleCase(humanReadableNumbers[minimum])) 
    }
    else
    {
        return minimum == 0 ? 
            string.Format("Less than {0}", humanReadableNumbers[maximum]) :
            string.Format("From {0} to {1}", humanReadableNumbers[minimum], humanReadableNumbers[maximum-1]);
    }
}
\$\endgroup\$
6
  • \$\begingroup\$ Interesting approach, I like it. I was looking for something that was simple to maintain, and this seems to do the trick. int? maximum isn't an optional argument, but that's just a minor change. \$\endgroup\$ – BCdotWEB May 20 '15 at 13:58
  • \$\begingroup\$ Oh, oops, somehow mis-read that as an optional argument. I'll update my answer \$\endgroup\$ – Nick Udell May 20 '15 at 14:02
  • 1
    \$\begingroup\$ I actually improved upon your solution when I was implementing it in my code: since a List<string> can be accessed by an index, I replaced the Dictionary<int, string> with a List<string> (List<string>{"zero", "one", "two" /*etc*/}; Works just as well. ;-) \$\endgroup\$ – BCdotWEB May 20 '15 at 14:20
  • \$\begingroup\$ It does work, provided you never intend to include negative numbers. Although, now I think about it, at that point you could manage the negative numbers using a special branch that adds "Negative {0}" and then accesses the List by Abs(minimum), for example, so a List would still be a sound choice, but it's worth bearing in mind. \$\endgroup\$ – Nick Udell May 20 '15 at 14:25
  • 1
    \$\begingroup\$ The redundant else should be removed. \$\endgroup\$ – Heslacher May 20 '15 at 16:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.