Refactor Large Switch Заявление - PullRequest
       29

Refactor Large Switch Заявление

1 голос
/ 27 апреля 2020

Я написал этот метод, который «угадывает» правильный значок шрифта, чтобы применить его к расходам, основанным на вводе данных пользователем при отправке формы - проверка выполняется перед вызовом этого метода. Если никакие условия не соответствуют, он возвращает общий значок c:

public static class IconService
{
    public static string GuessExpenseIcon(string input)
    {
        string expenseName = input.ToLower();
        string expenseIcon;

        switch (expenseName)
        {
            case string a when a.Contains("phone"):
            case string b when b.Contains("mobile"):
                expenseIcon = "fas fa-mobile-alt";
                break;

            case string a when a.Contains("rent"):
            case string b when b.Contains("mortgage"):
            case string c when c.Contains("house"):
            case string d when d.Contains("flat"):
            case string e when e.Contains("apartment"):
                expenseIcon = "fas fa-home";
                break;

            case string a when a.Contains("gas"):
            case string b when b.Contains("util"):
                expenseIcon = "fas fa-burn";
                break;

            case string a when a.Contains("electric"):
            case string b when b.Contains("power"):
                expenseIcon = "fas fa-bolt";
                break;

            case string a when a.Contains("petrol"):
            case string b when b.Contains("diesel"):
            case string c when c.Contains("fuel"):
                expenseIcon = "fas fa-gas-pump";
                break;

            case string a when a.Contains("food"):
            case string b when b.Contains("groceries"):
            case string c when c.Contains("eat"):
            case string d when d.Contains("take"):
                expenseIcon = "fas fa-utensils";
                break;

            case string a when a.Contains("water"):
                expenseIcon = "fas fa-shower";
                break;

            case string a when a.Contains("car"):
            case string b when b.Contains("van"):
                expenseIcon = "fas fa-car";
                break;

            case string a when a.Contains("internet"):
            case string b when b.Contains("network"):
                expenseIcon = "fas fa-wifi";
                break;

            case string a when a.Contains("spotify"):
                expenseIcon = "fab fa-spotify";
                break;

            case string a when a.Contains("bus"):
            case string b when b.Contains("coach"):
                expenseIcon = "fas fa-bus";
                break;

            case string a when a.Contains("charity"):
            case string b when b.Contains("donation"):
                expenseIcon = "fas fa-hand-holding-heart";
                break;

            case string a when a.Contains("aws"):
                expenseIcon = "fab fa-aws";
                break;

            default:
                expenseIcon = "fas fa-money-bill-alt";
                break;
        }

        return expenseIcon;
    }
}

Мой вопрос: является ли такой большой оператор switch лучшим способом для достижения этой цели?

Я знаю, что, возможно, просто преждевременно оптимизирую, поскольку я не заметил отрицательных результатов, но по какой-то причине мне это просто не кажется правильным.

Ответы [ 3 ]

4 голосов
/ 27 апреля 2020

Я бы определил словарь и использовал бы его:

public static class IconService
{
    private static Dictionary<string, string> _expenseIcons = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) {
        { "phone", "fa-mobile-alt" },
        { "mobile", "fa-mobile-alt" },
        { "rent", "fa-mobile-alt" },
        { "mortgage", "fa-mobile-alt" },
        { "house", "fa-mobile-alt" },
        { "flat", "fa-mobile-alt" },
        { "apartment", "fa-mobile-alt" }
        /* etc */
    };

    public static string GuessExpenseIcon(string input)
    {
        if (_expenseIcons.TryGetValue(input, out string expenseIcon)) // if the icon is found in the dictionary
        {
            return $"fas {expenseIcon}";
        }

        // default
        return "fas fa-money-bill-alt";
    }
}

Вы сказали, что вам нужно угадать его по строке, может работать наивный токенизация (разбиение по строке):

public static string GuessExpenseIcon(string input)
{
    string[] parts = input.Split();
    foreach (string part in parts)
    {
        if (_expenseIcons.TryGetValue(part, out string expenseIcon)) // if the icon is found in the dictionary
        {
            return $"fas {expenseIcon}";
        }
    }
    // default
    return "fas fa-money-bill-alt";
}

Я использовал регистр символов без учета регистра для ключа словаря, поэтому вам не нужно делать бит .ToLower(). Я также извлек обычную часть "fas" иконки из того, что мы храним в словаре, поскольку она есть у каждой иконки.

Не думаю, что это обязательно лучше, чем ваше утверждение switch , но это более элегантное решение, и оно также открывает возможность изменить настройку _expenseIcons. Например, вы можете загрузить его из конфигурационного файла и т. Д. c.

С точки зрения эффективности, мы инициализируем словарь один раз за время существования приложения / домена приложения. эффективность поиска в самом словаре близка к O (1):

Получение значения с помощью его ключа очень быстро, близко к O (1), потому что класс Dictionary<TKey,TValue> реализован в виде таблицы ha sh.

0 голосов
/ 27 апреля 2020

Как насчет этого?

class MatchPattern
{
    public string [] Patterns {get;set;}

    // This is to asume that you have more logic 
    // If all the logic of GetIcon is to return a simple string, you can
    // replace with 
    //  public string IconName {get;set;}
    // instead.
    public Func<string, string> GetIcon {get;set;}
}

public class IconService
{
    private MatchPatterns[] _patterns;
    public IconService()
    {
        _patterns=new []
        {
            new MatchPattern
            {
                Patterns=new[]{"phone", "mobile"},
                GetIcon=(x)=>"fas fa-mobile-alt"
            },
            new MatchPattern
            {
                Patterns=new[]{"rent", "mortgage", "house", "flat", "apartment"},
                GetIcon=(x)=>"fas fa-home"
            }
            , 
            // Here is more
        }
    }
    public static string GuessExpenseIcon(string input)
    {
        foreach(var pattern in _patterns)
        {
            if(pattern.Any(item=>input.contains(item))
            {
                return pattern.GetIcon.Invoke(input);
            }
        }

    }
}

Использование Fun c состоит в том, чтобы охватить сценарий, в котором у вас все еще есть немного отличающиеся логики c между различными случаями. Если это не так, вы можете использовать просто IconName вместо GetIcon Fun c.

class MatchPattern
{
    public string [] Patterns {get;set;}

    public string IconName {get;set;}
}
0 голосов
/ 27 апреля 2020

Вы можете работать со списком для каждого случая

foreach (var str in new string[] { "phone","mobile" }) {
    if (expenseName.Contains(str)){
        expenseIcon = "fas fa-mobile-alt";
    }
}

Возможно, стоит взглянуть на лямбда-выражения, у них более короткий синтаксис в Java, но я не уверен, насколько хорошо это поддерживается в C#

...