Programming
Java
C#
20 November 2019

Место enum в современном изменчивом мире


Есть у нас, допустим, компьютерная игра с разнополыми персонажами. Пол персонажа будет храниться в поле gender. Можно сделать это поле целым числом или строкой, но хороший программист стремится сделать некорректные состояния объектов невыразимыми, и поэтому, скорее всего, заведёт для пола enum. Теперь сделать персонажа с неправильным полом просто невозможно!


А ещё в игре есть база данных, в которой хранятся все игроки, и туда тоже нужно записывать, какого они пола. Было бы неплохо сделать так, чтобы персонажа с некорректным полом в базу данных добавить было нельзя, потому что с ней будет работать не только код, в котором для полов ничего, кроме enum, не предусмотрено, но ещё и живые люди. А живые люди, как известно, так и норовят вписать в базу данных то, что туда вписывать не надо.


К счастью, специально для таких случаев во многих СУБД также есть возможность делать колонки типа enum, тем самым ограничивая диапазон значений, которые могут иметь данные в такой колонке. Чем и воспользуется опытный разработчик, который не желает, чтобы в его проекте что-то было не по фэншую.


На клиенте программист тоже не дурак и тоже знает, что некорректные состояния должны быть невыразимы, и что для этого пол персонажа должен быть enum.


Идеально!


Однако если присмотреться, то можно обнаружить один небольшой изъян, который в итоге приведёт к проблемам. Поле, в котором хранится пол, называется не sex, а gender, а значит, к нему уже подбирается гейм-дизайнер. Рано или поздно он узнает, что гендеров, в отличии от полов, существенно больше двух, и захочет добавить гендер GENERIC_TEEN. Тут-то всем и поплохеет.


Клиента надо постоянно обновлять


Первыми под удар попадут ребята, которые писали клиентский код, потому что некорректные значения параметра гендер начнут приходить с сервера по каждому чиху. А когда с сервера придёт персонаж с гендером о существовании которого клиент не знает, то игра, как и хотел программист на клиенте, просто не запустится. Теперь при появлении новых гендеров необходимо релизить новые версии клиентов и запускать принудительные обновления, которые мы все так сильно любим.


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


Новый код не сможет работать со старой базой


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


Поэтому, в базе данных enum нам мешает, и в данном случае надо хранить в поле строки, или инты, или что угодно, во что при необходимости можно записать значение, не предусмотренное заранее.


И кто-то скажет: ладно, ладно, на клиенте enum вредит, и в базе данных он больше мешает, чем помогает, но в серверном коде-то enum приносит большую пользу. Он же не будет предлагать убрать enum из кода на сервере? Будет!


Старый код не сможет работать с новой базой


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


А если на сервере для гендера использовать enum, то всё опять развалится, только уже после того, как выяснится, что с новым кодом что-то не то и надо немедленно откатить всё до предыдущей версии, а новые гендеры в базу данных уже попали.


И поэтому enum из серверного кода мерным солдатским шагом идут куда-то в направлении ближайшего лесного массива, чтобы никогда оттуда не вернуться, ибо всё, что может развалить систему в неподходящий момент — зло, а злу не место в нашем уютном коде.


Так может, забить на enum и всегда использовать строки?


Короче, как сказал бы Винни-Пух – это какие-то неправильные енамы, и это они провоцируют программиста писать неправильный код! В enum надо помещать действительно незыблемые и фундаментальные вещи, которые не меняются годами, а не гендеры, которые добавляются и исчезают каждый релиз! Каждой задаче – свой инструмент, и enum – это не тот инструмент, который нужно использовать для хранения значений, возможный спектр которых постоянно расширяется.


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


Эта конкретная ситуация интересна тем, что ничто не предвещало добавления в enum новых значений, не говоря уже о том, что новые значения будут появляться регулярно. Откуда нам вообще было знать, что полов будет больше двух?


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


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


Получается, enum вообще не должно быть в коде, связанном с гендерами?


Несмотря на то, что я сейчас наговорил, enum в коде всё равно могут быть очень полезными, даже если в них находятся значения, которые попадают в базу данных. Не надо только использовать их для того, чтобы хранить значения, которые вытаскиваются из БД, надо сравнивать то, что пришло из БД с желаемым значением. Например, когда программист хочет написать, что если гендер персонажа – GENERIC_TEEN, то ему нельзя класть в инвентарь спиртные напитки, можно завести enum Gender с методом value, возвращающим строку, и написать код который будет проверять, равенство поля gender у персонажа и Gender.GENERIC_TEEN.value() и это будет хорошо, потому что не даст программисту сделать ошибку в значении статуса.


Код должен эволюционировать


Решения, хорошо служившие нам в какой-то момент, могут стать неудобными по прошествии времени. И нужно следить за тем, чтобы код отражал текущее состояние проекта, а не был набором костылей, с помощью которых решение, созданное на коленке пару лет назад, натягивается на “здесь и сейчас”. В противном случае он будет неисчерпаемым источником факапов и лулзов для всех, кто хоть как-то связан с проектом, будь то разработчик, тестировщик или непосредственный потребитель.


+7
6.8k 39
Comments 98
Top of the day