Рефакторинг операторов Switch и есть ли реальное использование для операторов Switch вообще?

28

Я читал эту статью, и мне было интересно, избавимся ли мы от всех операторов switch, заменив их словарем или фабрикой, чтобы в моих проектах вообще не было операторов switch.

Что-то не совсем складывалось.

Вопрос в том, имеют ли операторы switch какое-либо реальное применение, или мы идем дальше и заменяем их либо словарем, либо фабричным методом (при использовании фабричного метода, конечно, будет минимальное использование операторов switch для создания объектов используя фабрику ... но это все).

Kanini
источник
10
Предполагая, что вы реализуете фабрику, как вы решите, какой тип объекта создавать?
CodeART
2
Очень похожий: programmers.stackexchange.com/questions/146771/…
pdr
@CodeWorks: у меня, конечно, будут условия где-то, чтобы решить, какую конкретную реализацию использовать.
Канини
@CodeWorks: с виртуальным конструктором, очевидно. (И если вы не можете реализовать фабричный шаблон таким образом, вам нужен лучший язык.)
Мейсон Уилер

Ответы:

44

Оба switchутверждения и полиморфизм имеют свое применение. Обратите внимание, что существует и третий вариант (в языках, которые поддерживают указатели на функции / лямбды и функции более высокого порядка): сопоставление рассматриваемых идентификаторов с функциями-обработчиками. Это доступно, например, в C, который не является языком OO, и в C #, который *, но не (пока) в Java, который тоже является OO *.

В некоторых процедурных языках (не имеющих ни полиморфизма, ни функций более высокого порядка) switch/ if-elseоператоры были единственным способом решения класса проблем. Поэтому многие разработчики, привыкшие к такому мышлению, продолжали использовать switchдаже в ОО-языках, где полиморфизм часто является лучшим решением. Вот почему часто рекомендуется избегать / рефакторинга switchвысказываний в пользу полиморфизма.

В любом случае, лучшее решение всегда зависит от конкретного случая. Вопрос заключается в следующем: какой вариант дает вам более чистый, более лаконичный и более понятный код в долгосрочной перспективе?

Операторы переключения часто могут становиться громоздкими, имея десятки случаев, усложняя их обслуживание. Поскольку вы должны хранить их в одной функции, эта функция может стать огромной. Если это так, вам следует рассмотреть возможность рефакторинга в сторону решения на основе карты и / или полиморфного решения.

Если то же самое switchначинает появляться в нескольких местах, полиморфизм, вероятно, является наилучшим вариантом для объединения всех этих случаев и упрощения кода. Особенно, если ожидается, что в будущем будет добавлено больше дел; чем больше мест вам нужно обновлять каждый раз, тем больше возможностей для ошибок. Однако часто отдельные обработчики дел настолько просты, или их так много, или они настолько взаимосвязаны, что рефакторинг их в полную полиморфную иерархию классов является излишним, или приводит к большому количеству дублированного кода и / или запутанности, трудно поддерживать иерархию классов. В этом случае может быть проще использовать функции / лямбды (если ваш язык позволяет вам).

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

* Здесь я употребляю термин «ОО» свободно; Меня не интересуют концептуальные дебаты о том, что является «настоящим» или «чистым» ОО.

Петер Тёрёк
источник
4
+1. Кроме того, эти рекомендации, как правило, сформулированы так: « Предпочитаю, чтобы полиморфизм переключался», и это хороший выбор слов, в значительной степени соответствующий этому ответу. Он признает, что существуют обстоятельства, когда ответственный кодер может сделать другой выбор.
Карл Манастер
1
И бывают моменты, когда вам нужен подход с переключением, даже если в вашем коде их много. Я имею в виду кусок кода, который генерирует 3D-лабиринт. Использование памяти увеличилось бы, если бы однобайтовые ячейки в массиве были заменены классами.
Лорен Печтел
14

Это где я возвращаюсь, чтобы быть динозавром ...

Операторы Switch сами по себе не плохи, их использование - это вопрос.

Наиболее очевидным является «один и тот же» оператор switch, повторяемый снова и снова через ваш код, который является плохим (если бы он там делал, приложил бы все усилия, чтобы не повторить) - и это последний случай, с которым вы можете иметь дело с использованием полиморфизма. Обычно во вложенных делах есть что-то довольно ужасное (раньше у меня был абсолютный монстр - не совсем уверенный, как я справляюсь с этим сейчас, кроме «лучше»).

Словарь в качестве переключателя я считаю более сложным - в основном да, если ваш переключатель охватывает 100% случаев, но там, где вы хотите использовать варианты по умолчанию или не выполнять действия, он начинает становиться немного интереснее.

Я думаю, что вопрос заключается в том, чтобы избежать повторения и убедиться, что мы составляем наши графы объектов в нужных местах.

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

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

Murph
источник
4
У меня был учитель, который сказал, что в идеале код должен быть написан так, чтобы «кто-то, кто ничего не знал о программировании (например, ваша мать), мог понять это». К сожалению, это идеальный вариант, но, может быть, мы все равно должны включить наших мам в обзоры кода!
Майкл К
+1 за «но если вы придете к единственной строке кода, где вам нужно добавить что-то новое, вам придется перепрыгнуть повсюду, чтобы выяснить, что вам нужно добавить / изменить»
quick_now
8

Операторы Switch против subtype-polymorphism - старая проблема, на которую часто ссылаются в сообществе FP при обсуждении проблемы выражения .

В основном, у нас есть типы (классы) и функции (методы). Как мы кодируем вещи, чтобы потом было легко добавлять новые типы или новые методы?

Если вы программируете в стиле ОО, трудно добавить новый метод (поскольку это будет означать рефакторинг всех существующих классов), но очень легко добавить новые классы, которые используют те же методы, что и раньше.

С другой стороны, если вы используете оператор switch (или его OO-эквивалент, Observer Pattern), тогда очень легко добавлять новые функции, но сложно добавлять новые case / классы.

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

hugomg
источник
4
избавимся ли мы от всех операторов switch, заменив их словарем или фабрикой, чтобы в моих проектах вообще не было операторов switch.

Нет. Такие абсолюты редко бывают хорошей идеей.

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

Telastyn
источник
И действительно, если вы развернули фабрику, словарь и поиск, это, по сути, будет оператором switch: если array [hash (search)], то вызвать array [hash (search)]. Хотя это может быть расширенно во время выполнения, а скомпилированные ключи - нет.
Zan Lynx
@ZanLynx, полная противоположность верна, по крайней мере, с C #. Если вы посмотрите на IL, созданный для нетривиального оператора switch, вы увидите, что компилятор превращает его в словарь. Так как он быстрее.
Дэвид Арно
@DavidArno: "полная противоположность"? То, как я это читал, мы говорили точно так же. Как это может быть наоборот?
Зан Рысь
2

Учитывая, что это не зависит от языка, сквозной код лучше всего работает с switchоператорами:

switch(something) {
   case 1:
      foo();
   case 2:
      bar();
      baz();
      break;

   case 3:
      bang();
   default:
      bizzap();
      break;
}

Эквивалентно ifс очень неудобным регистром по умолчанию. И имейте в виду, что чем больше провалов, тем длиннее будет список условий:

if (1 == something) {
   foo();
}
if (1 == something || 2 == something) {
   bar();
   baz();
}
if (3 == something) {
   bang();
}
if (1 != something && 2 != something) {
   bizzap();
}

(Однако, учитывая то, что говорят некоторые другие ответы, я чувствую, что упустил суть вопроса ...)

Izkata
источник
Я бы посчитал использование сквозных операторов в a switchна том же уровне, что и goto. Если для выполнения алгоритма требуются потоки управления, которые соответствуют конструкциям структурированного программирования, следует использовать такие конструкции, но если алгоритм не соответствует таким конструкциям, использование gotoможет быть лучше, чем попытка добавить флаги или другую логику управления для соответствия другим структурам управления ,
суперкат
1

Операторы Switch как дифференциация регистра имеют реальное применение. Функциональные языки программирования используют так называемое сопоставление с образцом, которое позволяет определять функции по-разному в зависимости от ввода. Однако в объектно-ориентированных языках та же цель может быть достигнута более элегантно с помощью полиморфизма. Вы просто вызываете метод, и в зависимости от фактического типа объекта будет выполнена соответствующая реализация метода. Это является причиной идеи, что операторы switch являются запахом кода. Однако даже в ОО-языках вы можете найти их полезными для реализации шаблона абстрактной фабрики.

TL; DR - они полезны, но довольно часто вы можете сделать лучше.

scarfridge
источник
2
Я чувствую некоторую предвзятость - почему полиморфизм более элегантен, чем сопоставление с образцом? И что заставляет вас думать, что полиморфизм не существует в FP?
tdammers
@tdammers Прежде всего, я не хотел сказать, что полиморфизм не существует в FP. Haskell поддерживает специальный и параметрический полиморфизм. Сопоставление с образцом имеет смысл для алгебраического типа данных. Но для внешних модулей это требует выставления конструкторов. Очевидно, что это нарушает инкапсуляцию абстрактного типа данных (реализованного с помощью алгебраических типов данных). Вот почему я чувствую, что полиморфизм более элегантен (и возможен в FP).
шарфридж
Вы забываете полиморфизм через классы типов и экземпляры.
tdammers
Вы когда-нибудь слышали о проблеме выражения ? ОО и ФП лучше в разных ситуациях, если вы перестанете думать об этом.
hugomg
@tdammers О каком полиморфизме я говорил, когда говорил о специальном полиморфизме? Специальный полиморфизм реализуется в Haskell через классы типов. Как вы можете сказать, что я забыл это? Просто не правда.
шарфридж