Я думаю, это еще один вопрос о жестком кодировании и передовых методах. Допустим, у меня есть список значений, скажем, фруктов, хранящихся в базе данных (он должен быть в базе данных, так как таблица используется для других целей, таких как отчеты SSRS), с идентификатором:
1 Apple
2 Banana
3 Grapes
Я могу представить их пользователю, он выбирает один, он сохраняется в его профиле как FavouriteFruit, а идентификатор хранится в его записи в базе данных.
Когда речь идет о бизнес-правилах / доменной логике, каковы рекомендации для назначения логики определенным значениям. Скажем, если пользователь выбрал «Виноград», я хочу выполнить какое-то дополнительное задание, как лучше всего ссылаться на значение «Виноград»:
// Hard coded name
if (user.FavouriteFruit.Name == "Grapes")
// Hard coded ID
if (user.FavoriteFruit.ID == 3) // Grapes
// Duplicate the list of fruits in an enum
if (user.FavouriteFruit.ID == (int)Fruits.Grapes)
или что-то другое?
Поскольку, конечно, FavouriteFruit будет использоваться во всем приложении, список может быть добавлен или отредактирован.
Кто-то может решить, что он хочет переименовать «Виноград» в «Виноград», и это, конечно, нарушит опцию жестко закодированных строк.
Жестко закодированный идентификатор не совсем понятен, хотя, как показано, вы можете просто добавить комментарий, чтобы быстро определить, какой это элемент.
Опция enum подразумевает дублирование данных из базы данных, что может показаться неправильным, поскольку оно может быть не синхронизировано.
В любом случае, заранее спасибо за любые комментарии или предложения.
MyApplication.Grape.ID
заикается, так сказать. «Яблоко» не является «Red_Apple», так как ID 3 также равен 4. Таким образом, потенциал для переименования «Apple» в «Red_Apple» не имеет больше смысла, чем объявление, что 3 равно 4 (а может быть, даже 3). Смысл перечисления - абстрагировать его числовую ДНК. Так что, возможно, пришло время по- настоящему отделить произвольные ключи реляционных БД, которые буквально не имеют смысла в бизнес-моделях.Ответы:
Избегайте строк и магических констант любой ценой. Они полностью исключены, их даже не следует рассматривать как варианты. Похоже, что у вас остается только одна жизнеспособная опция: идентификаторы, то есть перечисления. Однако есть и еще один вариант, который, на мой взгляд, является лучшим. Давайте назовем эту опцию «Предварительно загруженные объекты». С предварительно загруженными объектами вы можете сделать следующее:
Что здесь произошло, так это то, что я, очевидно, загрузил весь ряд
Grape
в память, поэтому у меня есть его идентификатор, готовый для использования в сравнениях. Если вы используете Object-Relational Mapping (ORM), это выглядит еще лучше:(Вот почему я называю это «Предварительно загруженные объекты».)
Итак, во время запуска я загружаю все свои таблицы «перечисления» (небольшие таблицы, такие как дни недели, месяцы года, пол и т. Д.) В основной класс домена приложения. Я загружаю их по имени, потому что, очевидно,
MyApplication.Grape
должен получить строку под названием «Виноград», и я утверждаю, что каждый из них найден. Если нет, то мы имеем гарантированную ошибку во время запуска, которая является наименее злокачественной из всех ошибок времени выполнения.источник
Grape = fetchRow( Fruit.class, NameColumn, "Grape" );
и если вы сделать что-то неправильно,AssertionError
даст вам знать.enum
волшебная строка. Суть в том, чтобы сосредоточить все привязки по имени в одном месте , проверить их все во время запуска и обеспечить безопасность типов .Проверка на строку является наиболее читаемой, но она выполняет двойную функцию: она используется как в качестве идентификатора, так и описания (которое может измениться по несвязанным причинам).
Я обычно делю обе обязанности на отдельные поля:
Где описание может измениться (но не «Виноград» на «Банан»), но код не может быть изменен никогда.
Хотя это в основном потому, что наши идентификаторы почти всегда генерируются автоматически и поэтому не очень подходят. Если вы можете свободно выбирать идентификаторы, возможно, вы можете гарантировать их правильность и использовать их.
Кроме того, как часто кто-то действительно редактирует "Виноград" в "Виноград"? Возможно, ничего из этого не нужно.
источник
Здесь вы ожидаете, что логика программирования будет автоматически адаптироваться к изменяющимся данным. Простые статические параметры, такие как Enum, здесь не работают, потому что вы не можете реально добавлять дополнительные перечисления во время выполнения.
Несколько моделей, которые я видел:
В целом, мне нравится, что данные являются полными с точки зрения ссылок на действия, которые они подразумевают, даже если сами действия могут быть реализованы в другом месте. Любой код, который определяет действия, независимые от данных, только что осветил ваше представление данных, что, скорее всего, будет расходиться и привести к ошибкам.
источник
Хранить их в обоих местах (в таблице и в ENUM) не так уж и плохо. Аргументация следующая:
Храня их в таблице базы данных, мы можем обеспечить ссылочную целостность в базе данных через внешние ключи. Таким образом, когда вы связываете человека или какую-либо сущность с фруктом, это только фрукт, который существует в таблице базы данных.
Хранение их как ENUM также имеет смысл, потому что мы можем писать код без магических строк, и это делает код более читабельным. Да, они должны синхронизироваться, но на самом деле, как трудно будет добавить строку в ENUM и новый оператор вставки в базу данных.
Одна вещь, после определения ENUM, не меняйте его значение. Например, если у вас было:
НЕ переименовывайте Виноград в Виноград. Просто добавьте новый ENUM.
Если вам нужно перенести данные, примените обновление, чтобы переместить весь виноград в виноград.
источник
Вы правы, задавая этот вопрос, на самом деле это хороший вопрос, поскольку вы пытаетесь защититься от оценки неточных условий.
Тем не менее, оценка (ваши
if
условия) не обязательно должна быть в центре внимания, как вы обойти это. Вместо этого обратите внимание на то, как вы распространяете изменения, которые могут вызвать проблему «несинхронизации».Струнный подход
Если вы должны использовать строки, почему бы не раскрыть функциональность изменения списка через пользовательский интерфейс? Конструкция системы так , что при переходе
Grape
кGrapes
, например, обновить все записи в настоящее время ссылкиGrape
.Идентификационный подход
Я всегда предпочел бы ссылаться на идентификатор, несмотря на компромисс некоторой читабельности.
The list may be added to
снова может быть чем-то, о чем вы будете уведомлены, если обнаружите такую функцию пользовательского интерфейса. Если вас беспокоит изменение порядка элементов, изменяющих идентификатор, распространите такое изменение еще раз на все зависимые записи. Как и выше. Другой вариант (следуя надлежащему соглашению о нормализации, будет иметь ваш столбец enum / id - и ссылаться на более подробнуюFruitDetail
таблицу, в которой есть столбец 'Order', который вы можете найти).В любом случае, вы можете видеть, что я предлагаю контролировать изменение или обновление вашего списка. Делаете ли вы это с помощью ORM или другого доступа к данным, определяется спецификой вашей технологии. То, что вы, по сути, делаете, требует от людей, которые отошли от БД для таких изменений - что, я думаю, хорошо. Большинство основных CRM будут предъявлять те же требования.
источник
Очень распространенная проблема. Хотя дублирование клиентской части данных может показаться нарушением принципов DRY , это на самом деле связано с различием в парадигме между уровнями.
Не перечислять enum (или что-либо еще) с базой данных тоже не так уж редко. Возможно, вы выдвинули другое значение в таблицу метаданных для поддержки новой функции отчетов, которая еще не используется в коде на стороне клиента.
Иногда бывает и наоборот. Новое значение перечисления добавляется на стороне клиента, но обновление базы данных не может произойти до тех пор, пока администратор базы данных не сможет применить изменения.
источник
Если предположить, что мы говорим о том, что по сути является статическим поиском, то третий вариант - enum - в основном единственный разумный выбор. Это то, что вы будете делать, если база данных не участвует, так что это имеет смысл.
Тогда возникает вопрос о том, как синхронизировать перечисления и статические / справочные таблицы в базе данных, и, к сожалению, это не проблема, на которую у меня пока нет полного ответа.
По своему выбору я выполняю все операции по поддержке схемы в коде и, следовательно, могу поддерживать взаимосвязь между сборкой приложения и ожидаемой версией схемы, поэтому несложно поддерживать поиск и перечисление в синхронизации, но это то, что нужно запомнить делать. Было бы лучше, если бы он был более автоматизированным (а также автоматизированным интеграционным тестом, чтобы убедиться, что перечисления и совпадения поиска помогут), но я никогда не реализовывал это.
источник