Большие операторы Switch: Bad OOP? - PullRequest
73 голосов
/ 03 февраля 2009

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

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

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

Есть ли какие-либо шаблоны для такого рода проблем? Любые предложения о возможных реализациях?

Одна мысль, которую я имел, состояла в том, чтобы использовать поиск по словарю, сопоставляя текст команды с типом объекта для создания экземпляра. Это имеет приятное преимущество, заключающееся в простом создании нового объекта и вставке новой команды / типа в таблицу для любых новых команд.

Однако это также имеет проблему взрыва типа. Теперь мне нужно 100 новых классов, плюс я должен найти способ аккуратно связать их с моделью данных. Является ли «один истинный оператор переключения» действительно правильным выбором?

Буду признателен за ваши мысли, мнения или комментарии.

Ответы [ 14 ]

33 голосов
/ 03 февраля 2009

Вы можете получить некоторую выгоду от Командного паттерна .

Для ООП вы можете объединить несколько похожих команд, каждая из которых в один класс, если изменения поведения достаточно малы, чтобы избежать полного взрыва класса (да, я уже слышу, как гуру ООП уже кричат ​​об этом). Однако, если система уже ООП и каждая из 100+ команд действительно уникальна, просто сделайте их уникальными классами и воспользуйтесь наследованием для консолидации общих вещей.

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

24 голосов
/ 03 февраля 2009

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

switch (eFoo)
{
case Foo.This:
  eatThis();
  break;
case Foo.That:
  eatThat();
  break;
}

switch (eFoo)
{
case Foo.This:
  drinkThis();
  break;
case Foo.That:
  drinkThat();
  break;
}

... возможно, следует переписать как ...

IAbstract
{
  void eat();
  void drink();
}

class This : IAbstract
{
  void eat() { ... }
  void drink() { ... }
}

class That : IAbstract
{
  void eat() { ... }
  void drink() { ... }
}

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

16 голосов
/ 03 февраля 2009

Команда может быть одной из 100 различных команд

Если вам нужно выполнить одно из 100 различных действий, вы не можете избежать наличия ветки со 100 путями. Вы можете кодировать его в потоке управления (switch, if-elseif ^ 100) или в данных (100-элементная карта от строки до команды / фабрики / стратегии). Но это будет там.

Вы можете попытаться изолировать результат ветви с 100 путями от вещей, которые не должны знать этот результат. Может быть, просто 100 различных методов это хорошо; нет необходимости изобретать ненужные объекты, если это делает код громоздким.

3 голосов
/ 03 февраля 2009

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

2 голосов
/ 07 февраля 2009

Когда мы говорим о большом операторе switch, на ум приходят две вещи:

  1. Это нарушает OCP - вы могли бы постоянно поддерживать большую функцию.
  2. У вас может быть плохая производительность: O (n).

С другой стороны, реализация карты может соответствовать OCP и может работать с потенциально O (1).

2 голосов
/ 03 февраля 2009

Я вижу шаблон стратегии. Если у меня есть 100 различных стратегий ... пусть будет так. Заявление о гигантском переключении ужасно. Являются ли все команды допустимыми именами классов? Если это так, просто используйте имена команд в качестве имен классов и создайте объект стратегии с Activator.CreateInstance.

1 голос
/ 12 февраля 2009

Вы можете использовать словарь (или хэш-карту, если вы кодируете на Java) (это называется разработкой на основе таблиц Стива Макконнелла).

1 голос
/ 03 февраля 2009

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

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

Конечно, полностью диспетчер команд ООП (основанный на магии, такой как отражение или механизмы, такие как активация Java), более красив, но иногда вам просто нужно что-то исправить и выполнить работу;)

0 голосов
/ 03 июля 2014

Подумайте, как Windows изначально была написана в насосе сообщений приложения. Это отстой. Приложения будут работать медленнее с добавлением дополнительных опций меню. По мере того, как искомая команда заканчивалась все дальше и дальше к нижней части оператора switch, ожидание ответа становилось все дольше. Недопустимо иметь длинные операторы переключения, точка. Я создал демон AIX в качестве обработчика команд POS, который мог обрабатывать 256 уникальных команд, даже не зная, что находится в потоке запросов, полученных по TCP / IP. Самым первым символом потока был индекс в массиве функций. Для любого неиспользуемого индекса был установлен обработчик сообщений по умолчанию; войти и сказать до свидания.

0 голосов
/ 17 сентября 2013

У меня недавно была похожая проблема с огромным оператором switch, и я избавился от уродливого переключателя с помощью самого простого решения таблицы поиска и функции или метода, возвращающего значение вы ожидаете. шаблон команды - хорошее решение, но я думаю, что иметь 100 классов нехорошо. поэтому у меня было что-то вроде:

switch(id)
    case 1: DoSomething(url_1) break;
    case 2: DoSomething(url_2) break;
    ..
    ..
    case 100 DoSomething(url_100) break;

и я изменил на:

string url =  GetUrl(id);  
DoSomthing(url);

GetUrl может перейти в БД и вернуть URL-адрес, который вы ищете, или может быть словарём в памяти, содержащим 100 URL-адресов. Я надеюсь, что это может помочь кому-то там, когда заменит огромные чудовищные заявления переключателя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...