- Альтернатива вложенным операторам Switch в Java
- 3 ответов
- Альтернатива Switch Case в Java
- 14 ответов
- ООП или как избавиться от switch -case
- ❗ Удалите из кода If-Else и Switch Case
- Что плохого в традиционном ветвлении?
- Какие есть альтернативы?
- Моделирование концепций с помощью простых классов
- Изменение реализации метода объекта в зависимости от его состояния
- Рефакторинг ветвлений на отдельные классы
- Заключение
- Полное руководство по switch-выражениям в Java 12
- Предварительный обзор
- Проблема с операторами в switch
- Представляем switch-выражения!
- Выражение или оператор
- Стрелка или двоеточие
- Подробнее об эволюции switch
- Несколько меток на case
- Типы за пределами Enum
- Подробнее о стрелке
- Отсутствие сквозного перехода к следующему case
- Блоки операторов
- Подробнее о выражениях switch
- Множественные выражения
- Ранний возврат
- Покрытие всех вариантов
- Размышление
Альтернатива вложенным операторам Switch в Java
поэтому я написал метод сегодня, который включал использование вложенных операторов switch, и код выглядел довольно чистым и кратким для меня, но мне сказали, что вложенные операторы switch обычно не лучший способ пойти, поскольку они могут запутаться с большим количеством операторов switch, которые вы добавляете. Вот пример того, как выглядел мой код:
таким образом, мой вопрос был бы, по существу, что было бы подходящей альтернативой этим операторам switch?
3 ответов
Я рекомендую вам заменить каждый вложенный оператор switch вызовом процедуры, которая затем выполняет вложенный код коммутатора.
написать что-то вроде этого:
- как много становится довольно трудно читать.
- и каждый раз, когда возникает новый случай, мы должны изменить код и добавить случай
мы можем рассмотреть возможность использования polymorphism в таких случаях
Я собираюсь дать простой класс, чтобы вы поняли. Предположим, что класс ранее с switch case
теперь мы заменим этот переключатель нашей логикой полиморфизма
теперь у нас есть тестовый класс без случая переключателя
и даже если нам нужно добавить дополнительные случаи, мы должны просто добавить реализации без изменений здесь
Смотрите полный код и посмотреть, если это возможно сделать
Если у вас есть целые числа X и Y, и вам нужно включить оба, вы можете объединить их каким-то однозначным образом и включить комбинацию. Например, если y
Источник
Альтернатива Switch Case в Java
есть ли какой-либо альтернативный способ реализовать случай коммутатора в Java, Кроме если еще что-то не выглядит хорошо. Набор значений будет там в комбинации, согласно выбору, соответствующий метод должен быть выполнен.
14 ответов
предположительно, вы боретесь с требованием, чтобы случай был постоянным. Как правило, это код запах, но есть вещи, которые вы можете сделать. Возможно, вы захотите поднять и связать с другим вопросом, который подробно объясняет, почему вы пытаетесь переключиться.
В приведенном выше примере вы можете сопоставить с «обработчиками», что-то вроде
что затем делает все это превращается в поиск.
опять же, это немного запах, поэтому, пожалуйста, разместите вопрос, который иллюстрирует рассуждения.
если у вас есть много операторов switch/case вокруг вашего кода, и они сводят вас с ума.
предположим, у вас есть часть программного обеспечения, которое используется для сохранения информации на разных устройствах: 4 операции сохранения определены:загрузка, сохранение, удаление, обновление, который может быть реализован N числом механизм сохранения (плоские файлы, сеть, СУБД, XML и т. д.).
ваш код должен поддерживать их все, так что в 4 разных местах, у вас есть это:
до
то же самое для сохранения/удаления/обновления
использование оператора switch / case становится проблематичным:
каждый раз, когда вы хотите добавить новый тип, вы должны вставить новый переключатель / корпус в каждый раздел.
много раз, некоторые типы похожи, и им не нужен другой переключатель / случай (вы можете каскадировать их )
таким образом, рефакторинг здесь должен был бы добавить интерфейс или абстрактный тип и иметь различные типы, реализующие этот интерфейс и делегирующие ответственность перед этим объектом.
таким образом, у вас будет что-то вроде этого:
после
и различные реализации
и другие типы будут реализованы в соответствии с их логикой. Сеть будет иметь дело с сокетами и потоками, DB будет иметь дело с JDBC, ResultSets и т. д. XML с узлом etc.так далее.
и, наконец, вам просто нужно делегировать вызовы.
Итак, вам просто нужно создать правильный экземпляр для менеджера сохраняемости в соответствии с типом только один раз. Тогда все призывы разрешаются полиморфизмом. Это одна из ключевых особенностей объектно-ориентированной технологии.
если вы решите, что вам нужен другой менеджер сохраняемости, вы просто создадите новую реализацию и назначите ее классу.
рефакторинг вашего кода для использования полиморфизма может избавиться от необходимости оператора switch. Тем не менее, есть некоторые законные использования для коммутатора, поэтому это зависит от вашей ситуации.
Источник
ООП или как избавиться от switch -case
Помощь в написании контрольных, курсовых и дипломных работ здесь.
Как в as3 можно избавиться от оператора switch-case?
Добрый день! Использую в программе фабрику создания экземпляров животных public class.
Как объединить несколько функций в операторе Switch case или if
Доброго времени суток! Добавлено через 7 минут Доброго времени суток! Помогите у меня есть.
Как сделать возможность использовать в функции нужный массив без кучи if или switch-case?
Есть несколько массивов и одна функция.Как сделать возможность использовать в функции нужный массив.
switch-case, как писать в самом case
Такс дело вот в чём,прога работает всё хорошо,вот только мне не нравится как записваю страны в.
Вот есть у меня класс «tracker». Там реализовано как раз меню без case:
switch case 1 ИЛИ 2
делаю разбор вводимых команд, некоторые обрабатываются похоже, команды однобуквенные, разбираю.
If или switch().case. Что быстрее
Есть два кода. Первый: if(a == 2) a += 2; if(a == 3) a+= 3; if(a == 4) a+=4; Второй:
Что лучше использовать? if-else, switch-case или class?
Что используется чаще в програмирование когда есть много вариантов выбора? К примеру программа: .
Статический хеш или switch-case/if-else для выбора
Добрый день. Положим есть такой код:void parseByType(const QString &data, const QString &type) <.
Оператор Switch Case или недогоняю условие задачи
Добрый день. Дана задача. (C# console) Оператор Switch Case Составить программу, которая.
Источник
❗ Удалите из кода If-Else и Switch Case
Перевод публикуется с сокращениями, автор оригинальной статьи Nicklas Millard.
C корее всего, i f- e lse и switch – ваш обычный подход к ветвлению кода, но в нем нет необходимости. Вы можете полностью исключить ключевое слово else из своего словаря по программированию. Некоторые матерые кодеры говорят, что i f-else – полиморфизм новичков.
Что плохого в традиционном ветвлении?
Много чего. Традиционное ветвление быстро разрастается. Вам придется изменять существующий код каждый раз при добавлении новой функции. Это нарушает принцип Open-Closed. Функции должны быть реализованы с помощью новых классов.
Какие есть альтернативы?
Существует масса альтернатив, но мы рассмотрим три типовых подхода, которые часто применяются при удалении традиционного ветвления из кода:
- концепции моделей с классами;
- использование полиморфного выполнения при работе с изменяющимися состояниями объектов;
- инкапсуляция стратегии в отдельные классы.
Эти 3 подхода легко справятся с большинством повседневных ситуаций, с которыми вы можете столкнуться.
Все методы имеют общие черты:
- Новая функциональность реализуется с помощью новых классов. Добавление кода, а не его изменение, обычно является более безопасным вариантом. Каждый раз, когда изменяется уже используемый код, вы подвергаетесь огромному риску все сломать.
- Проще тестировать специализированные классы – это огромное преимущество. Простые классы и методы легче рассматривать – легче понять множество небольших и сплоченных классов, чем несколько монолитных.
- Управление концепциями на низком уровне лучше, чем расширение обязанностей одного класса.
Моделирование концепций с помощью простых классов
Допустим, у нас есть класс User и в нем имя пользователя. Оно является строкой и имеет два условия: не может быть нулем или пустой строкой и не может превышать 50 символов.
Можно с уверенностью предположить, что нам понадобятся имена пользователей в других частях приложения. Каждый раз, когда мы получаем имя, придется выполнять одни и те же проверки, которые будут разбросаны по всему проекту.
Если что-то изменится и возникнет необходимость в валидации специальных символов, таких как « æøå » , придется найти каждое место, где получено имя пользователя, и добавить новую проверку.
Гораздо лучшим подходом является перенос концепции имени пользователя и создание небольшого специализированного объекта, как показано ниже.
Этот фрагмент кода, несомненно, чище. Теперь каждый раз, когда потребуется обновление, его нужно будет делать только в одном месте.
Изменение реализации метода объекта в зависимости от его состояния
Иногда необходимо, чтобы объект вел себя по-разному в зависимости от его внутреннего состояния. Типичный ленивый способ реализации этого – традиционное ветвление, как в приведенном ниже примере.
Всякий раз, когда нам нужно вывести деньги, в аккаунте следует проверить внутреннее состояние и вести себя соответственно.
Паттерн состояния – это очень крутой подход к удалению сумасшедшей вложенной условной логики. Пытайтесь добиться, чтобы наш код был похож на следующий:
Видите, какая плоская конструкция?
Теперь каждая ветвь инкапсулирована в собственный класс, а класс account делегирует ответственность специализированному AccountState .
Удвойте количество кода и повысьте читабельность.
У нас есть базовый класс, от которого наследуется каждый объект состояния. Если мы получим новый запрос на добавление состояния RequiresValidation или что-то еще, будет легко реализовать эту функцию, не касаясь существующих классов.
Внимательный читатель заметил, что нет никакого перехода в состояние. Очевидно, что классы не полностью реализованы, т. к. переход из одного состояния в другое выходит за рамки статьи.
Рефакторинг ветвлений на отдельные классы
Наиболее часто используемый способ устранения условных ветвлений – объекты стратегии.
Допустим, мы хотим преобразовать любой тип в формат CSV и указать, как преобразуется каждое свойство типа, но тип не должен определять это сам. [Csv Info] – это атрибут, который по сути является метаинформацией о типе. Затем эта метаинформация считывается с помощью крошечной рефлексии внутри метода ToCsv() .
Ниже приведен фрагмент класса CsvInfoAttribute . Этот код не является полным мусором, но он не очень расширяемый и не очень гибкий. Каждый раз, когда нужно будет добавить новый параметр преобразования, придется добавлять и дополнительный enum, а затем реализовывать преобразование для него в методе Format() . Это означает, что вам нужно изменить существующий код.
Предположим, что класс живет в какой-то общей библиотеке, от которой зависит много проектов в вашей организации. Если одной команде нужен новый вариант преобразования, ей придется обратиться к сопровождающим проекта, чтобы они добавили функциональность, создали новую версию и опубликовали ее.
Мы можем сделать это, разделив каждую ветку switch/case на специализированные классы, что делает enum ненужным.
Каждая стратегия должна реализовывать общий интерфейс, а CsvInfoAttribute больше не должен иметь собственного метода Format() . Вместо этого он делегирует ответственность за форматирование специализированным объектам.
Рассмотрим приведенный ниже код:
Чтобы это сработало, необходимо определить некоторые ограничения: любой форматер должен реализовать IValueFormatter и иметь конструктор по умолчанию.
Представьте, как легко сейчас протестировать и исправить ошибки. Вы всегда будете точно знать, что проверять и где искать. Больше никаких следов безумной switch-логики.
Заключение
К сожалению, традиционное ветвление используется очень широко. Многие разработчики придерживаются своего испытанного и верного способа ветвления кода, не осознавая, что обслуживание и добавление функциональности превратилось в кошмар.
Если вы действительно хотите стать сильным разработчиком, начните искать способы устранения традиционного ветвления или используйте описанные в статье .
Источник
Полное руководство по switch-выражениям в Java 12
Старый добрый switch был в Java с первого дня. Мы все используем его и привыкли к нему — особенно к его причудам. (Кого-нибудь еще раздражает break ?) Но теперь все начинает меняться: в Java 12 switch вместо оператора стал выражением:
В switch появилась возможность возвращать результат своей работы, который можно присвоить переменной; вы также можете использовать синтаксис в стиле «лямбда», который позволяет избавиться от сквозного прохода по всем case , в которых нет оператора break .
В этом руководстве я расскажу Вам обо всем, что необходимо знать о switch-выражениях в Java 12.
Предварительный обзор
Это означает, что данная управляющая конструкция может быть изменена в будущих версиях спецификации языка.
Что бы начать использовать новую версию switch необходимо применить опцию командной строки —enable-preview как во время компиляции, так и во время запуска программ (также необходимо использовать —release 12 при компиляции — примечание переводчика).
Так что имейте ввиду, что switch, как выражение, не имеет на данный момент окончательного варианта синтаксиса в Java 12.
Если у вас возникло желание поиграть со всем этим самим, то вы можете посетить мой демо-проект Java X на гитхабе.
Проблема с операторами в switch
Прежде, чем мы перейдем к обзору нововведений в switch, давайте быстро оценим одну ситуацию. Допустим, мы столкнулись с «ужасным» тернарным булеаном и хотим преобразовать его в обычный булеан. Вот один из способов сделать это:
Согласитесь, что это очень неудобно. Как и многие другие варианты switch, встречающиеся в «природе», представленный выше пример просто вычисляет значение переменной и присваивает его, но реализация обходная (объявляем идентификатор result и используем его позже), повторяющаяся (мои break ‘и всегда результат copy-pasta) и подвержена ошибкам (забыл еще одну ветку? Ой!). Тут явно есть, что улучшить.
Давайте попробуем решить эти проблемы, поместив switch в отдельный метод:
Так намного лучше: отсутствует фиктивная переменная, нет break ‘ов, загромождающих код и сообщений компилятора об отсутствии default (даже если в этом нет необходимости, как в данном случае).
Но, если подумать, то мы не обязаны создавать методы только для того, чтобы обойти неуклюжую особенность языка. И это даже без учёта, что такой рефакторинг не всегда возможен. Нет, нам нужно решение получше!
Представляем switch-выражения!
Как я показал в начале статьи, начиная с Java 12 и выше, вы можете решить вышеуказанную проблему следующим образом:
Я думаю, что это довольно очевидно: если ternartBool равен TRUE , то result ‘у будет присвоено true (иными словами TRUE превращается в true ). FALSE становится false .
Сразу возникают две мысли:
- switch может иметь результат;
- что там со стрелками?
Прежде чем углубляться в детали новых возможностей switch, в начале я расскажу об этих двух основных аспектах.
Выражение или оператор
Возможно, вы удивлены, что switch теперь является выражением. А чем же он был до этого?
До Java 12 switch был оператором — императивной конструкцией, регулирующей поток управления.
Думайте о различиях старой и новой версии switch, как о разнице между if и тернарным оператором. Они оба проверяют логическое условие и выполняют ветвление в зависимости от его результата.
Разница в том, что if просто выполняет соответствующий блок, тогда как тернарный оператор возвращает какой-то результат:
То же самое для switch: до Java 12, если вы хотели вычислить значение и сохранить результат, то должны были либо присвоить его переменной (а затем break ), либо вернуть из метода, созданного специально для оператора switch .
Теперь же всё выражение оператора switch оценивается (выбирается для выполнения соответствующая ветка), и результат вычислений может быть присвоен переменной.
Еще одним отличием между выражением и оператором является то, что выражение switch, поскольку оно является частью оператора, должно заканчиваться точкой с запятой, в отличие от классического оператора switch.
Стрелка или двоеточие
В вводном примере использовался новый синтаксис в лямбда-стиле со стрелкой между меткой и выполняющейся частью. Важно понимать, что для этого не обязательно использовать switch в качестве выражения. Фактически, пример ниже эквивалентен приведенному в начале статьи коду:
Обратите внимание, что теперь вы можете использовать break со значением! Это идеально согласуется с инструкциями switch старого стиля, которые используют break без какого-либо значения. Так в каком случае стрелка означает выражение вместо оператора, для чего она здесь? Просто хипстерский синтаксис?
Исторически сложилось, что метки с двоеточием просто отмечают точку входа в блок операторов. С этого места начинается выполнение всего кода ниже, даже когда встречается другая метка. В switch нам это известно, как сквозной переход к следующему case (fall-through): метка case определяет, куда перепрыгивает поток управления. Для его завершения нужен break или return .
В свою очередь, использование стрелки означает, что будет выполнен только блок справа от нее. И никакого «проваливания».
Подробнее об эволюции switch
Несколько меток на case
До сих пор каждый case содержал только одну метку. Но теперь все изменилось — один case может соответствовать нескольким меткам:
Поведение должно быть очевидным: TRUE и FALSE приводят к одному и тому же результату — вычисляется выражение «sane».
Это довольно приятное нововведение, которое пришло на смену множественному использованию case , когда требовалось реализовать сквозной переход к следующему case .
Типы за пределами Enum
Все примеры со switch в этой статье используют enum . А как насчет других типов? Выражения и операторы switch также могут работать с String , int , (проверьте документацию) short , byte , char и их обертками. Здесь пока что ничего не изменилось, хотя идеи об использовании таких типов данных, как float и long по прежнему остаются в силе (со второго по последний абзац).
Подробнее о стрелке
Давайте рассмотрим два свойства, характерных для стрелочной формы записи разделителя:
- отсутствие сквозного перехода к следующему case ;
- блоки операторов.
Отсутствие сквозного перехода к следующему case
Вот, что говорится в JEP 325 об этом:
Текущий дизайн оператора switch в Java тесно связан с такими языками, как C и C++ и по умолчанию поддерживает сквозную семантику. Хотя этот традиционный способ управления часто полезен для написания низкоуровневого кода (такого как парсеры для двоичного кодирования), поскольку switch используется в коде более высокого уровня, ошибки такого подхода начинают перевешивать его гибкость.
Я полностью согласен и приветствую возможность использовать switch без поведения по умолчанию:
Важно усвоить, что это не имеет никакого отношения к тому, используете ли вы switch в качестве выражения или оператора. Решающим фактором тут является стрелка против двоеточия.
Блоки операторов
Как и в случае с лямбдами, стрелка может указывать либо на один оператор (как выше), либо на блок, выделенный фигурными скобками:
Блоки, которые приходится создавать для многострочных операторов имеют дополнительное преимущество (что не требуется при применении двоеточия), которое заключается в том, что для использования одинаковых имен переменных в разных ветках, switch не требует специальной обработки.
В случае, если вам показался необычным способ выхода из блоков с помощью break , а не через return , то не переживайте — меня это тоже озадачило и показалось странным. Но потом я задумался и пришел к выводу, что это имеет смысл, поскольку сохраняет старый стиль конструкции switch , которая использует break без значений.
Подробнее о выражениях switch
И последнее, но не менее важное — особенности использования switch в качестве выражения:
- множественные выражения;
- ранний возврат (досрочный return );
- охват всех значений.
Обратите внимание, что при этом не имеет значения, какая форма используется!
Множественные выражения
Switch-выражения являются множественными выражениями. Это означает, что они не имеют своего собственного типа, но могут быть одним из нескольких типов. Наиболее часто в качестве таких выражений используются лямбда-выражения: s -> s + » » , могут быть Function , но также могут быть Function или UnaryOperator .
С помощью switch-выражений тип определяется по взаимодействию между тем, где используется switch и типами его веток. Если switch-выражение назначается типизированной переменной, передается в качестве аргумента или иным образом используется в контексте, где известен точный тип (это называется целевым типом), то все его ветки должны соответствовать этому типу. Вот что мы делали до сих пор:
Как итог — switch присваивается переменной result типа String . Следовательно, String является целевым типом, и все ветки должны возвращать результат типа String .
То же самое происходит и здесь:
А что произойдет сейчас?
(Про использование типа var читайте в нашей прошлой статье 26 рекомендаций по использованию типа var в Java — примечание переводчика)
Если целевой тип неизвестен, из-за того, что мы используем var, тип вычисляется путем нахождения наиболее конкретного супертипа из типов, создаваемых ветками.
Ранний возврат
Следствием различия между выражением и оператором switch является то, что вы можете использовать return для выхода из оператора switch :
… вы не можете использовать return внутри выражения …
Это имеет смысл независимо от того, используете ли вы стрелку или двоеточие.
Покрытие всех вариантов
Если вы используете switch в качестве оператора, тогда не имеет значения, охвачены все варианты или нет. Конечно, вы можете случайно пропустить case , и код будет работать неправильно, но компилятору все равно — вы, ваша IDE и ваши инструменты анализа кода останетесь с этим наедине.
Switch-выражения усугубляют эту проблему. Куда следует перейти switch , если нужная метка отсутствует? Единственный ответ, который может дать Java — это возвращать null для ссылочных типов и значение по умолчанию для примитивов. Это породило бы множество ошибок в основном коде.
Чтобы предотвратить такой исход, компилятор может помочь вам. Для switch-выражений компилятор будет настаивать, чтобы все возможные варианты были охвачены. Давайте посмотрим на пример, который может привести к ошибке компиляции:
Интересным является следующее решение: добавление ветки default , конечно, исправит ошибку, но это не является единственным решением — еще можно добавить case для FALSE .
Да, компилятор наконец-то сможет определить, охватываются ли все значения enum (исчерпывают ли все варианты), и не установить бесполезные значения по умолчанию! Давайте посидим минуту в безмолвной благодарности.
Хотя, это все же вызывает один вопрос. Что делать, если кто-то возьмет и превратит сумасшедший Bool в кватернионный Boolean, добавив четвертое значение? Если вы перекомпилируете switch-выражение для расширенного Bool, то получите ошибку компиляции (выражение больше не является исчерпывающим). Без перекомпиляции это превратится в проблему во время выполнения. Чтобы отловить эту проблему, компилятор переходит в ветку default , которая ведет себя так же, как та, которую мы использовали до сих пор, вызывая исключение.
В Java 12 охват всех значений без ветки default работает только для enum , но когда switch в будущих версиях Java станет более мощным, он также сможет работать с произвольными типами. Если метки case ‘ов смогут не только проверять равенство, но и проводить сравнения (например _ …) — это позволит охватить все варианты для числовых типов.
Размышление
Из статьи мы узнали, что Java 12 превращает switch в выражение, наделяя его новыми возможностями:
- теперь один case может соответствовать нескольким меткам;
- новая стрелочная форма case … -> … следует синтаксису лямбда-выражений:
- допускаются однострочные операторы или блоки;
- предотвращается сквозной переход к следующему case ;
- теперь всё выражение оценивается, как значение, которое затем может быть присвоено переменной или передано, как часть более крупного оператора;
- множественное выражение: если целевой тип известен, то все ветки должны ему соответствовать. В противном случае определяется конкретный тип, который соответствует всем веткам;
- break может возвращать значение из блока;
- для выражения switch использующее enum , компилятор проверяет охват всех его значений. Если default отсутствует, добавляется ветка, которая вызывает исключение.
Куда это нас приведет? Во-первых, поскольку это не окончательная версия switch , у вас все еще есть время, чтобы оставить отзыв в списке рассылки Amber, если вы с чем-то не согласны.
Затем, предполагая, что switch остается таким, каким он является в данный момент, я думаю, что стрелочная форма станет новым вариантом по умолчанию. Без сквозного перехода к следующему case и с лаконичными лямбда-выражениями (это очень естественно иметь case и один оператор в одной строке) switch выглядит намного компактнее и не ухудшает читаемость кода. Я уверен, что буду использовать только двоеточие, если у меня возникнет необходимость в сквозном проходе.
Что вы думаете? Довольны тем, как все сложилось?
Источник