«Таблица поиска функций» вместо переключателей - PullRequest
7 голосов
/ 10 марта 2011

Недавно я наткнулся на некоторый код, который заменяет использование переключателей на жесткое кодирование

Dictionary<string (or whatever we would've been switching on), Func<...>> 

, и где бы он ни был, вместо этого он диктует ["значение"].(...).В некотором роде код кажется неправильным, но в то же время методы выглядят немного чище, особенно когда есть много возможных случаев.Я не могу дать никакого объяснения, почему это хороший или плохой дизайн, поэтому я надеялся, что кто-то может дать некоторые причины для поддержки / осуждения такого рода кода.Есть ли прирост производительности?Потеря ясности?

Пример:

public class A {
    ...
    public int SomeMethod(string arg){
        ...
        switch(arg) {
            case "a": do stuff; break;
            case "b": do other stuff; break;
            etc.
        }
        ...
    }
    ...
}

становится

public class A {

    Dictionary<string, Func<int>> funcs = new Dictionary<string, Func<int>> {
        { "a", () => 0; },
        { "b", () => DoOtherStuff(); }
        ... etc.
    };

    public int SomeMethod(string arg){
        ...
        funcs[arg].Invoke();
        ...
    }
    ...
}

Ответы [ 6 ]

6 голосов
/ 10 марта 2011

Преимущества:

  1. Вы можете изменить поведение во время выполнения «переключателя» во время выполнения
  2. это не загромождает методы, использующие его
  3. вы можете иметь не буквальные случаи (например, case a + b == 3) с гораздо меньшими хлопотами

Недостатки:

  1. Все ваши методы должны иметь одинаковую подпись.
  2. У вас есть изменение области действия, вы не можете использовать переменные, определенные в области действия метода, пока вы не захватите их в лямбду, вам придется позаботиться о переопределении всех лямбд, если вы добавите переменную в какой-то момент
  3. вам придется иметь дело именно с несуществующими индексами (аналогично default в switch)
  4. трассировка стека будет более сложной, если возникнет необработанное исключение, что приведет к усложнению отладки приложения

Должны ли вы использовать это? Это действительно зависит. Вам придется определить словарь в каком-то месте, поэтому код будет где-то загроможден. Вам придется решить для себя. Если вам нужно переключить поведение во время выполнения, словарное решение действительно выпадает, особенно если используемые вами методы не имеют побочных эффектов (т. Е. Не нуждаются в доступе к переменным в области видимости).

5 голосов
/ 10 марта 2011

По нескольким причинам:

  1. Поскольку выполнение этого способа позволяет вам выбрать, что каждая ветвь case будет делать во время выполнения .В противном случае вы должны скомпилировать его.
  2. Более того, вы также можете изменить число ветвей во время выполнения.
  3. Код выглядит намного чище, особенно с большимиколичество ветвей, как вы упомянули.

Почему это решение кажется вам неправильным?Если словарь заполняется во время компиляции, то вы наверняка не потеряете безопасность (входящие делегаты обязательно должны скомпилироваться без ошибок).Вы делаете немного теряете производительность, но:

  1. В большинстве случаев потеря производительности не является проблемой
  2. Гибкость, которую вы получаете, огромна
2 голосов
/ 18 июля 2011

Никто еще ничего не сказал о том, что я считаю самым большим недостатком этого подхода.

Это менее ремонтопригодно.

Я говорю это по двум причинам.

  1. Синтаксически сложнее.
  2. Требуется больше рассуждений, чтобы понять.

Большинство программистов знают, как работает оператор switch.Многие программисты никогда не видели словарь функций.

Хотя это может показаться интересной и новой альтернативой оператору switch и вполне может быть единственным способом решения некоторых проблем, он значительно сложнее.Если вам не нужна дополнительная гибкость, не используйте ее.

2 голосов
/ 10 марта 2011

У Джона есть пара хороших ответов. Вот еще немного:

  • Всякий раз, когда вам нужен новый регистр в коммутаторе, вы должны кодировать его в этот оператор коммутатора. Это требует открытия этого класса (который раньше работал просто отлично), добавления нового кода, повторной компиляции и повторного тестирования этого класса и любого класса, который его использовал. Это нарушает правило разработки SOLID, принцип Open-Closed (классы должны быть закрыты для модификации, но открыты для расширения). Словарь делегатов, напротив, позволяет добавлять, удалять и заменять делегатов по своему усмотрению, не изменяя код, выполняющий выбор.
  • Использование словаря делегатов позволяет выполнять код в условиях, где бы он ни находился, и таким образом передавать его в словарь из любого места. Учитывая эту свободу, легко превратить дизайн в шаблон Стратегии, где каждому делегату предоставляется уникальный класс, который выполняет логику для этого случая. Это поддерживает инкапсуляцию кода и принцип единой ответственности (класс должен делать одну вещь и должен быть единственным классом, ответственным за эту вещь).
1 голос
/ 10 марта 2011

Если число возможных случаев больше, тогда лучше заменить Switch утверждение на strategy pattern, См. .

Применение шаблона стратегии вместо использования операторов переключения

0 голосов
/ 10 марта 2011

Преобразуйте ваш A класс в частичный класс и создайте второй частичный класс в другом файле, включив в него только словарь делегатов.

Теперь вы можете изменить количество ветвей и добавить логику в оператор switch, не касаясь источника для остальной части вашего класса.

...