Ваши способы написания действительно гибкого и масштабируемого кода
Первый вопрос, который приходит вам в голову: почему следует избегать конструкций switch-case?
Представьте, что вы работаете над проектом своей мечты, и все работает нормально. Вы делаете свою постоянную реализацию функций. Но тогда ваш ум поражает:
Почему я постоянно добавляю новые кейсы в любые сменные кейсы?
Почему я постоянно меняю код, который раньше работал нормально?
Перенесемся на несколько месяцев после прочтения этой статьи, и вы действительно сможете создавать надежные и масштабируемые приложения. Код, написанный один раз, используется повторно, пока не сгорит дерево вызовов. Добавление новых функций без изменения старого кода. Гарантирована даже понятная история git, и проект / игра / приложение эволюционировали, а не видоизменялись.
И все потому, что вы научились избегать использования переключателя.
Причина позади
Вы, наверное, поняли корпуса переключателей, потому что они не требуют особого обучения и обеспечивают довольно удобное использование. Это набор инструментов по умолчанию для начинающих и программистов среднего уровня. Но у них есть свои недостатки.
Они отлично работают при создании вашего начального набора кейсов, но как только вам нужно их расширить, они становятся неудобными и раздувают ваш код.
Кроме того, когда вы хотите отобразить сложные сценарии, случай переключения - беспорядок.
За несколько лет разработки на C # я много использовал кейсы switch и несколько раз сталкивался с недостатками switch-кейсов:
- Отсутствие использования переменных означает меньшую гибкость и жестко запрограммированные случаи
- Отсутствие использования одной и той же константы в нескольких случаях означает меньшую гибкость во время выполнения
- Не использовать выражения отношения (==,! =, ‹= И т. Д.)
- Отсутствие использования чисел с плавающей запятой или констант
- Невозможно расширить за счет переопределения среды, необходимо переопределить корпус коммутатора
Основная проблема корпуса переключателя
Представим, что мы хотим создать коллекционную карточную игру и вот-вот реализуем основную часть этой игры.
Вытягивание карт из колоды во время хода.
Следовательно, нам нужен класс CardDeck
, из которого мы позже сможем брать карты.
CardDeck
- это класс, в котором есть поле amountOfCards
для количества карт, оставшихся для розыгрыша.
Делаем это с помощью переключателя - чехол
Чтобы перестать быть простым программистом, мы должны сначала думать как базовый программист.
Следовательно, первый подход направлен на использование корпуса выключателя.
Функция Draw()
проверит этот факт и примет колоду карт и тип карт в качестве параметров:
Каждый рисунок должен сообщать нам, какую карту мы нарисовали. Будет 4 варианта:
- Монстр-карта
- Magic-Card
- Карта-ловушка
- Земельная карта
Если осталась хотя бы 1 карта, мы получаем случайную запись cardTypes
, которая попадает в case-case. Независимо от того, что было нарисовано, вызов PutCardToHand()
распечатает тип.
Теперь функция Main
вызывает этот код. Сначала мы помещаем эту функцию рисования в открытый класс SwitchCaseUser
, что означает создание экземпляра:
Кроме того, cardTypes
определены здесь как массив строк. Этот код приводит к
Хотя шнур работает нормально и делает то, для чего он был разработан, мы сталкиваемся с тремя серьезными проблемами:
- Корпус переключателя жестко запрограммирован: определение типов для рисования снаружи сделает эту функцию тесно связанной с этим внешним миром. Откуда этой функции знать, какие карты она вытягивает? Изменение внешнего пространства повлечет за собой изменение корпусов внутри этой функции.
- Определение типов карточек, которые можно было бы нарисовать внутри этой функции, приводит к той же проблеме, потому что теперь функция точно знает, что можно нарисовать. Но почему функция рисования должна это знать? Функция рисования должна рисовать, что бы она ни рисовала. В этом случае смена типа карты означает также смену корпуса переключателя.
- Расширять это поведение неудобно, а код негибкий. Потому что, если у нас есть полный функциональный класс, который делает именно то, что должен делать, и делает это хорошо, он должен оставаться таким, как есть навсегда. Мы должны программировать так, чтобы код работал и всегда должен работать. Не прикасайтесь к нему снова, если внешнее поведение изменится. Это означает передачу всех необходимых ссылок для достижения этого результата и независимости от того, что было передано.
Избегание случайного переключателя поможет устранить все упомянутые недостатки.
Как избежать переключателя - 5 возможностей
Мы снова передаем колоду карт и типы карт в нашу Draw()
функцию, но на этот раз мы не используем случай переключения. Вместо этого мы используем пять различных методов, чтобы избежать переключения.
Каждая глава функции и каждая ее первая часть будут одинаковыми для всех пяти методов:
Внутри оператора if идет сменная часть:
Первый и второй варианты, позволяющие избежать случаев переключения, сохраняют исходный строковый массив cardTypes
. Все остальные примеры основаны на преобразовании в List<string>
.
Это лишь краткое сравнение значимых замен корпуса выключателя. Если вам нужно более глубокое объяснение каждой техники, вы можете найти их в книге «Чистый код» Роберта К. Мартина.
# 1 For-Loop / ForEach-Loop
Цикл for выполняет итерацию по массиву, сравнивает cardType
со всеми записями из cardTypes
и передает совпадение функции PutCardToHand
.
# 2 Локальная функция
Локальная функция делает почти то же самое, что и цикл for, но инкапсулирует ее в локальную функцию с помощью цикла forEach.
Некоторые злоумышленники могут утверждать, что это просто сложный способ сделать это, как №1, но это демонстрация, и локальные функции становятся интересными в более сложном варианте использования.
# 3 Метод IndexOf
Преобразование array
в list
сейчас.
Поскольку у нас есть список, мы можем использовать некоторые предыдущие функции для их агрегирования.
Наш cardTypesList
теперь может получить доступ к набору функций (полное руководство в документации Microsoft). Один из них - это метод indexOf
, который возвращает индекс элемента, который соответствует нашему вводу.
Передача этого в нашу функцию PutCardToHand
приведет к желаемому поведению.
# 4 Анонимный метод
Анонимный метод похож на Хитрого Койота и его непонятного оружия, чтобы поймать дорожного бегуна. Мип Мип.
Мы должны иметь дело с определением накладных расходов.
Анонимная функция внутри именованной функции в качестве параметра приводит к двойному определению функции неисправности.
Это довольно сложный способ программирования и, следовательно, его нелегко понять впоследствии. Будьте внимательны!
((Func<string (inputParameter1), List<string> (inputParameter2), string (return parameter)>) anonymousFunction(inputParameter1, inputParameter2, returnParameter) { Lambda Expression returning cardType
}(passingParameter1, passingParameter2) <- Instant Invokation
Типичное использование этой анонимной функции - это когда передается большой набор данных, и нам нужно агрегировать только подмножество свойств только в этой конкретной части кода, или другой типичный случай, когда вам нужно только что-то сделать. с данными только здесь и прямо сейчас один раз.
Следовательно, определение именованной функции и загрязнение пространства вашего класса не требуется.
Более простой способ - сохранить анонимную функцию внутри делегата, а затем вызвать ее так:
Тем не менее, анонимные функции должны быть кратковременными.
# 5 Лямбда-выражение
Анонимные функции в этом сценарии длинны и излишни. Я тебя прикрыл. Следовательно, лямбда-выражение легко и подходит для такого рода задач.
Посмотрите, как быстро и быстро вы получите результат.
Это простой лайнер с использованием метода Find()
для списков.
То, что верно в отношении анонимных функций, также верно и здесь, поскольку лямбда-выражения являются расширенной анонимной функцией.
Звонок в главном - Избегайте финальной стадии переключения
Поскольку это не случай переключения, я упаковал все альтернативы в класс SwitchCaseAvoider
.
Внутри main мы называем его так же, как и другой:
Приведет к
Вывод
Не все из них - лучший способ подойти к этой конкретной ситуации.
Некоторые из них (например, цикл for или цикл forEach) становятся интенсивными в вычислениях, если вы пытаетесь использовать их в больших наборах данных для получения одного совпадения. Также есть встроенные функции (например, для коллекций).
Что у них общего:
Их можно расширить, просто переопределив среду, а не саму функцию.
Следовательно, здесь выполняется гораздо более важный принцип, чем поиск наилучшего способа решения конкретной проблемы.
Ваши программы не решают конкретных задач; они всегда решат сложную задачу или несколько задач. Гораздо важнее соблюдение общих принципов, таких как создание игр с моей серией SOLID в Unity.
Спасибо за то, что прочитали, и если вы хотите получить более выдающийся обработанный программный контент, посетите мои приключения на Udemy о C # и SQL.