Автор: Sergey Teplyakov

В обсуждении одного из моих ответов на ru.stackoverflow в G+ был поднят вопрос по поводу того, является ли оператор switch design или code smell-ом?

Тут нужно быстро вспомнить, откуда ноги вообще растут (ИМХО). В наших с вами разных языках программирования существует много разных способов решения одной и той же проблемы. Например, когда у нас есть определенная задача (нарисовать фигуру?!) и несколько разновидностей входных данных (круг, квадрат, прямоугольник?), то решить ее можно разными способами. Можно взять впихнуть тип в структурку и в методе draw перебрать все возможные варианты.

С точки зрения серьезного объектно-ориентированного программиста, такой подход считается негодным, нерасширяемым и вообще, неправильным. Ибо об этом писал дядюшка Боб Мартин, который будет потом приходить по ночам в страшных снах и мучить бедных программистов страшными вещами, типа добавлением новой фигуры каждые 42 минуты.

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

Так, например, проблема с фигурками и их рисованием заключается в том, что в будущем *предполагается* добавление новых фигур и не предполагается добавление новых операций над этими фигурами. В этом случае, для уменьшения стоимости внесения изменений и дается совет по выделению иерархии наследования.

Но даже в этом случае нужно понимать, на какой компромисс мы идем: иерархия наследования позволяет легко добавить новый тип фигуры (минимум изменений), но усложняет добавление новых операций (много изменений во множестве типов). В этом плане подход на основе объектов упрощает изменение системы в одну сторону, а тот же структурный или функциональный подход в другую (в этом случае проще будет добавлять именно новую операцию, а не новый тип). Эта проблема широко известна под названием Expression Problem, а сравнение ФП и ОО подходов я делал в статье OCP: ФП vs. ООП.

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

Тут, как всегда, все зависит от контекста, решаемой задачи и того, что в этом switch-е происходит.

Вот, например, если у вас есть фабрика, которая создает экземпляр некоторого объекта по входным данным, например, некоторый парсер в зависимости от расширения файла:public class ParserFactory
{
public static IParser CreateParser(string fileName)
{
switch (Path.GetExtension(fileName))
{
case '.txt':
return new TextParser(),
case '.xml':
return new XmlParser(),
default:
throw new NotSupportedException(),
}
}
}

Кто-то скажет, что решение #нерасширябельное, нарушает SRP и вообще, никуда не годится.

На самом деле, сделать такой вывод лишь по этому фрагменту нельзя. Если подобный switch лишь один, то с кодом все в порядке. Бертран Я могу и ФП тоже Мейер назвал это принципом единственного выбора, который заключается в следующем: если некоторый выбор находится в одном месте, то код зашибись. Если он начинает растекаться по коду, то это плохо и нужно что-то менять.

Но просто подумайте, а какие здесь альтернативы: прикрутить полиморфизм? Фабрику? А кто будет создавать фабрику? Что, MEF прикручивать и саморегистриуемые компоненты писать? Да, а крыша не поеедет это все сопровождать? Да, можно прикрутить словарик, но даже в этом случае он уместен, когда кода на каждый кейс много или самих кейсов сильно больше 2-х или 3-х.

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

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

Помогла статья? Оцените её!
0 из 5. Общее количество голосов - 0
 

You have no rights to post comments

Дмитрий Крикунов

Публикую статьи, обучающие курсы и новости по программированию: алгоритмам, языкам (С++, Java), параллельному программированию, паттернам и библиотекам (Qt, boost).