Куда вы помещаете данные своего словаря?

Допустим, у меня есть набор стран в моем приложении. Я ожидаю, что эти данные будут меняться, но не очень часто. Другими словами, я не смотрю на этот набор как на оперативные данные (например, я бы не стал предоставлять CRUD-операции для Country).

Тем не менее, я должен хранить эти данные где-то. Я вижу два способа сделать это:

  • Управляется базой данных. Создайте и заполните таблицу Country. Предоставьте какой-нибудь DAO для доступа к нему (findById()?). Таким образом, клиентский код должен будет знать идентификатор страны (который также может быть именем или кодом ISO). На стороне приложения у меня будет класс Country.

  • Управляется приложением. Создайте Enum, где я могу перечислить все страны, известные моей системе. Он также будет храниться в БД, но разница будет заключаться в том, что теперь клиентский код не должен иметь метод поиска (findById, findByName и т. д.) и жестко заданный идентификатор, имена или коды ISO. Он будет напрямую ссылаться на конкретную страну.

Я склоняюсь ко второму решению по нескольким причинам. Как ты это делаешь?

Правильно ли называть это «словарными данными»?

Приложение: Одна из основных проблем здесь заключается в том, что если у меня есть метод поиска, такой как findByName("Czechoslovakia"), то после 1992 года он ничего не вернет. Я не знаю, как отреагирует на это клиентский код (ведь он вроде как всегда ожидает вернуть Country, потому что, ну, это словарные данные). Еще хуже, если у меня есть что-то вроде findById(ID_CZ). Найти все эти зависимости будет очень сложно.

Если я уберу Country.Czechoslovakia из своего перечисления, я заставлю себя позаботиться о любой зависимости от Чехословакии.


person Georgy Bolyuba    schedule 25.02.2009    source источник


Ответы (7)


Это не поможет вам, но это зависит...

- Что вы собираетесь делать с этими странами?

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

- Собираетесь ли вы перевести названия стран на несколько языков?

-Будет ли бизнес-логика вашего приложения зависеть от выбранной страны? -Вам нужен кантри-класс?

так далее...

Без дополнительной информации я бы начал с Enum с несколькими странами и рефакторингом в зависимости от моих потребностей...

person pgras    schedule 25.02.2009

В некоторых приложениях, над которыми я работал, в базе данных была единственная таблица Enum, содержащая все данные этого типа. Он просто состоял из двух столбцов: EnumName и Value и заполнялся следующим образом:

  • "Страна", "Германия"
  • «Страна», «Великобритания»
  • «Страна», «США».
  • «Фруктовый», «Яблочный».
  • «Фруктовый», «Банановый»
  • «Фруктовый», «Апельсиновый»

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

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

person saw-lau    schedule 25.02.2009
comment
Это решение может быть интересно для Enum, которые часто меняются. Перечисление страны, скорее всего, останется стабильным в течение длительного времени. - person Georgy Bolyuba; 25.02.2009

Если оно не будет меняться очень часто и вы можете позволить себе отключить приложение для применения обновлений, я бы поместил его в перечисление Java и написал свои собственные методы для findById(), findByName() и так далее.

Преимущества:

  • Быстро - нет доступа к БД для инвариантных данных (или требования кэширования);
  • Простой;
  • Хорошо работает с инструментами рефакторинга.

Недостатки:

  • Нужно сбить приложение для обновления.

Если вы поместите данные в собственный jar-файл, обновление будет таким же простым, как обновление jar-файла и перезапуск приложения.

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

Если вы беспокоитесь о том, чтобы это перечисление было «синхронизировано» с базой данных, напишите интеграционный тест, который проверяет именно это, и регулярно запускайте его (например, на вашей машине CI).

person Dan Vinton    schedule 25.02.2009
comment
Для меня часть создания перечисления заключается в том, что мне не понадобятся findById(), findByName(). Вам не нужно делать findByName(USA), у вас есть Country.USA. Идентификаторы здесь тоже не нужны, как я это вижу. - person Georgy Bolyuba; 25.02.2009
comment
Это верно, если поле-член вашего объекта относится к этому типу перечисления, но иногда это не так — например, сторонний компонент может представлять страны в виде кодов ISO, которые вы хотите преобразовать в свое перечисление. - person Dan Vinton; 25.02.2009
comment
Согласен, но тут у меня, наверное, редкая возможность изобрести велосипед. Просто ищу лучший способ сделать это. И клиентский код, и реализация Country находятся под нашим контролем. - person Georgy Bolyuba; 25.02.2009

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

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

person Paul Tomblin    schedule 25.02.2009
comment
Не обязательно быть файлом свойств — подойдет любой текстовый файл, который вы можете легко разобрать. - person Jon Skeet; 25.02.2009
comment
Насколько эффективны методы поиска файлов свойств? Есть ли простой способ превратить файл свойств в карту? В ObjC это две строки кода или около того. - person Paul Tomblin; 25.02.2009

У меня, вероятно, был бы текстовый файл, встроенный в мою банку. Я бы загрузил его в память при запуске (или при первом использовании). В этот момент:

  • Это легко изменить (даже тем, у кого нет знаний в области программирования)
  • Легко обновить даже без полного повторного развертывания — поместите только текстовый файл где-нибудь в пути к классу.
  • Не требуется доступ к базе данных

РЕДАКТИРОВАТЬ: Хорошо, если вам нужно обратиться к данным конкретной страны из кода, то либо:

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

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

person Jon Skeet    schedule 25.02.2009
comment
Основная проблема здесь заключается не в сохранении данных, а скорее в методах поиска (findByName(USA)) по сравнению с зависимостью от конкретного экземпляра Enum (Country.USA) - person Georgy Bolyuba; 25.02.2009

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

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

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

РЕДАКТИРОВАТЬ: Из комментариев: «Конечно, я буду использовать ограничения внешнего ключа в своей БД. Но это можно сделать с использованием или без использования перечислений на стороне приложения»

Ах, я пропустил этот момент, читая второй пункт вашего вопроса. Однако я все же считаю, что лучше загружать их на карту, в основном на основе DRY. В противном случае, когда тот, кто должен его поддерживать, доберется до добавления новой страны, он обязательно обновится в одном месте, а не в другом, и будет ломать голову, пока не поймет, что ему нужно обновить его в двух разных местах. Пример преждевременной оптимизации. ИМХО, выигрыш в производительности будет минимальным за счет менее поддерживаемого кода.

person Evan    schedule 25.02.2009
comment
Конечно, я буду использовать ограничения внешнего ключа в своей БД. Но это можно сделать с использованием или без использования перечислений на стороне приложения. - person Georgy Bolyuba; 25.02.2009
comment
Что ж, следуя принципу DRY, я бы не стал предоставлять какой-либо сценарий инициализации для таблицы Country и заставлю ее создаваться при запуске (например, с использованием JPA с Hibernate). - person Georgy Bolyuba; 25.02.2009
comment
Не знаком с JPA. Если он создаст его при первом запуске приложения, то, думаю, все в порядке. Вы не хотели бы постоянно удалять и воссоздавать таблицу и ограничения. - person Evan; 25.02.2009

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

person Tom Hawtin - tackline    schedule 25.02.2009
comment
Я согласен начать с простого решения, за исключением того, что я думаю, что будет сложно преобразовать клиентский код из одного подхода в другой. - person Georgy Bolyuba; 25.02.2009