Рекомендации по использованию и сохранению перечислений


Я видел здесь несколько вопросов/дискуссий о лучшем способе обработки и сохранения значений , подобных перечислениям (например, сохранение данных, подходящих для перечислений, как сохранить перечисление с помощью NHibernate), и я хотел бы спросить, Что такое общий консенсус.

в частности:

  • как эти значения должны обрабатываться в коде?
  • как они должны быть сохранены в базе данных (как текст как число)?
  • каковы компромиссы различных решения?

Примечание: я переместил объяснения, первоначально включенные в этот вопрос, в ответ.

10 72

10 ответов:

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

исходная статья выглядит нормально для меня. Тем не менее, основываясь на комментариях, кажется, что некоторые комментарии относительно перечислений Java могут прояснить несколько вещей.

Enum type в Java-это класс по определению, но многие программисты склонны забывать об этом, потому что они скорее относятся к "списку допустимых значений", как и в некоторых других языках. Дело не только в этом.

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

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

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

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

в обработке кода для C# вы пропустили определение удаления значения 0. Я почти в обязательном порядке всегда объявляю свое первое значение как:

public enum SomeEnum
{
    None = 0,
}

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

Java или C# всегда должны использовать перечисления в коде. Отказ от ответственности: мой фон-C#.

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

значения всегда должны сохраняться в базе данных как целочисленные значения, чтобы защитить от рефакторинга имен перечисления. Держите документацию на каждом перечисление в Вики и добавление комментария к полю базы данных, указывающего на страницу вики, документирующую тип. Также добавьте XML-документацию к типу перечисления, содержащему ссылку на запись wiki, чтобы она была доступна через Intellisense.

Если вы используете инструмент для создания кода CRUD, он должен быть способен определять тип перечисления для столбца, чтобы сгенерированные объекты кода всегда использовали перечисляемые члены.

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

  • если у вас есть перечисление MyEnum, создайте статический класс MyEnumInfo, который предлагает служебные методы для обнаружения дополнительной информации о члене перечисления с помощью операторов switch или любых других необходимых средств. Добавление "Info" в конец имени перечисления в имени класса гарантирует, что они будут рядом друг с другом в IntelliSense.
  • украсьте элементы перечисления атрибутами для указания дополнительных параметров. Например, мы разработали элемент управления EnumDropDown, который создает ASP.NET раскрывающийся список заполнен значениями перечисления, а атрибут EnumDisplayAttribute указывает форматированный текст отображения, используемый для каждого элемента.

Я не пробовал этого, но с SQL Server 2005 или более поздней версии вы теоретически можете зарегистрировать код C# в базе данных, которая будет содержать информацию о перечислении и возможность преобразования значений в перечисления для использования в представлениях или других конструкциях, что делает метод перевод данных таким образом, чтобы было проще использовать базы данных.

Я попытался обобщить свое понимание. Не стесняйтесь редактировать это, если у вас есть какие-то корректировки. Так вот он идет:

код

в коде перечисления должны обрабатываться либо с использованием собственного типа перечисления языка (по крайней мере, в Java и C#), либо с использованием чего-то вроде "typesafe enum pattern". Использование простых констант (целочисленных или подобных) не рекомендуется, так как вы теряете безопасность типа (и затрудняете понимание того, что значения являются законным вводом, например, для метода).

выбор между этими двумя зависит от того, сколько дополнительных функций прилагается к перечислению:

  • если вы хотите поместить множество функций в перечисление (что хорошо, потому что вы избегаете switch () ing на нем все время), класс обычно более подходит.
  • С другой стороны, для простых значений, подобных перечислению, перечисление языка обычно более четкое.

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

сохранение перечисления

для сохранения перечислений каждому значению перечисления должен быть присвоен уникальный идентификатор. Это может быть либо целое число, либо короткая строка. Предпочтительна короткая строка, так как она может быть мнемонической (облегчает работу с базами данных и т. д. чтобы понять необработанные данные в децибел.)

  • в программном обеспечении каждое перечисление должно иметь функции сопоставления для преобразования между перечислением (для использования внутри программного обеспечения) и значением ID (для сохранения). Некоторые фреймворки (например, (N)Hibernate) имеют ограниченный support для автоматического выполнения этого. В противном случае, вы должны поместить его в перечисление типа/класса.
  • база данных должна (в идеале) содержать таблицу для каждого перечисления с перечислением юридических значений. Один столбец будет идентификатором (см. выше), который является PK. Дополнительные столбцы могут иметь смысл, например, для описания. Все столбцы таблицы, которые будут содержать значения из этого перечисления, могут затем использовать эту "таблицу перечисления" в качестве FK. Это гарантирует, что неправильные значения перечисления никогда не могут быть сохранены, и позволяет БД "стоять самостоятельно".

одна проблема с этим подходом заключается в том, что список допустимых значений перечислимого существует в двух местах (код и база данных). Это трудно избежать, и поэтому часто считается приемлемым, но есть два альтернативы:

  • только сохранить список значений в БД, генерировать тип перечисления во время сборки. Элегантный, но означает, что для запуска сборки требуется подключение к БД, что кажется проблематичным.
  • Определите список значений в коде, который должен быть авторитетным. Проверьте значения в БД во время выполнения (обычно при запуске), пожаловаться/прервать на несоответствие.

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

Ну, из моего опыта, используя перечисления для чего-либо, кроме передачи параметров (как флаги) для немедленного вызова метода, приводит к switch - ing в какой-то момент.

  • если вы собираетесь использовать перечисление по всему коду, то вы можете в конечном итоге с кодом, который не так легко поддерживать (печально известный switch заявление)
  • расширение перечислений-это боль. Вы добавляете новый элемент перечисления и в конечном итоге проходите через весь свой код, чтобы проверить все условия.
  • С .NET 3.5 вы можете добавить методы расширения в перечисления, чтобы они вели себя немного больше как классы. Однако добавление реальной функциональности таким образом не так просто, так как это все еще не класс (вы бы в конечном итоге использовали switch - es в ваших методах расширения, если не в другом месте.

таким образом, для перечислительного объекта с немного большей функциональностью вы должны потратить некоторое время и создать его как класс, имея в виду несколько вещей:

  • сделать ваш класс ведет себя как перечисление, вы можете либо заставить каждый производный класс создавать экземпляр как синглтон, либо переопределить Equals, чтобы разрешить сравнение значений разных экземпляров.
  • если ваш класс похож на перечисление, это должно означать, что он не должен содержать сериализуемого состояния - десериализация должна быть возможна только из его типа (своего рода "ID", как вы сказали).
  • логика персистентности должна быть ограничена только базовым классом, иначе расширение вашего "перечисления" было бы кошмаром. В случае, если вы пошли на одноэлементный шаблон, вам нужно будет обеспечить правильную десериализацию в одноэлементные экземпляры.

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

имхо, что касается части кода:

вы должны всегда используйте тип "enum" для ваших перечислений, в основном вы получаете много халявы, если вы это делаете: безопасность типа, инкапсуляция и предотвращение переключения, поддержка некоторых коллекций, таких как EnumSet и EnumMap и ясности кода.

как для сохранения части вы всегда можете сохранить строковое представление перечисления и загрузить его обратно с помощью перечисления.метод valueOf (String).

Я знаю, что это старый форум, Что делать, если база данных может иметь другие вещи, интегрирующиеся непосредственно в него? Например, когда результирующая БД является единственной целью кода. Затем вы будете определять перечисления при каждой интеграции. Лучше тогда иметь их в БД. В противном случае, я согласен с оригинальным постом.