Посты / Статические словари в программировании. Лучшие практики.

24.09.2018 15:29
Привет друзья. Я обратил внимание на то, что очень многие программисты пытаются засунуть все данные в БД (база данных) даже тогда, когда в этом нет острой необходимости. Есть очень много всевозможных типов данных, которые очень редко меняются и поэтому нет никакой необходимости хранить их в таблицах, писать к ним интерфейсы и вообще усложнять приложение. Для этих целей как раз и существует простой подход хранения данных в статических словарях. Об этом я и хочу сегодня поговорить.

На сегодняшний день очень много проектов переходят с классического использования шаблонов для бэкенда на использование API плюс фронтенд. Плюсы очевидны: полное отделение бэкенда от фронтенда, нет необходимости подгонять бэкенд для мобильной версии или десктопной. И очень часто для того, чтобы построить на фронтенде какие-либо списки, меню, селекторы и прочее, необходимо получить с бэкенда словарь.

Так вот, словари можно условно разделить на два типа:
  первый - это словарь с часто меняющимися данными, например, модели смартфонов (динамический словарь);
  второй - это словарь с редко меняющимися данными, например, цвет, размер экрана, производитель (статический словарь)

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

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

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

Ответ, как для меня, вполне очевиден - нет, не стоит!

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

Что такое статический словарь? Это по сути класс, в котором есть константа, которая представляет из себя хэш/словарь/многомерный ассоциативный массив (название зависит от ЯП, но суть одинакова).

И есть в этом классе конструктор, в котором происходят разные манипуляции, которые необходимы для построения вашей логики. Это может быть построение коллекции для Laravel или присвоение данных аксессору для Ruby on Rails. Не суть важно какой ЯП, логика использования везде одинакова.

Давайте перейдем к примеру. Пример будет на языке Ruby для фреймворка Ruby on Rails (RoR), но для Laravel на PHP логика будет та же, только синтаксис будет отличаться.

Постоим для начала базовый класс словаря dicts/dicts.rb:
# Base dicts class
class Dicts; end
Это просто базовый класс, в нашем случае пустой. Именно от него мы будем наследовать наши словари. Зачем он нам нужен? Он нужен для того, чтобы в будущем мы могли сюда поместить некоторые полезные методы, которые нам помогут реализовать нашу логику. И эти методы мы сможем легко использовать в самих классах словаря. То есть мы создали такой себе абстрактный класс.

Теперь создадим сам словарь, например, size_dict.rb:
class SizeDict < Dicts
attr_reader :dict

SIZE = [
small: {
value: 'small',
  name: 'Маленький'
  },
medium: {
value: 'medium',
  name: 'Средний'
  },
large: {
value: 'large',
  name: 'Большой'
  }
].freeze

def initialize
@dict = SIZE
end
end
Что здесь происходит? Мы фактически создали хэш, который присвоили константе. А в конструкторе создали геттер, которому и присвоили значение константы.

Теперь в контроллере, или в любом другом месте, мы можем легко получить данные словаря:
@size_dict = SizeDict.new.dict
Что нам это дает? Это дает возможность легко вывести словарь в виде JSON и передать его фронтенду, например, ReactJS, где можно будет легко построить список выбора размеров.

Почему в словаре происходит дублирование ключа в поле value? Для этого есть простое объяснение. По сути, для построения списка выбора, ключ в хэше вообще не нужен, достаточно только иметь значение value, но для поиска по статическому словарю нам таки, как раз, и нужен этот ключ. Так как поиск по ключу, а не по значению, очень быстро работает.

Как видите, само построение статического словаря очень простое и гибкое решение. Теперь поговорим о том, как значение из словаря сохранять в таблице БД.

Есть два варианта. Первое - это с контролем (ограничением) сохраняемых данных, и второе - без контроля. Что эти значит. Для первого случая должно использоваться ENUM поле, в котором перечислены все допустимые значения. В нашем случае для PostgreSQL 10 это будет выглядеть так:
CREATE TYPE types.size_enum AS ENUM (
'small',
'medium',
'large'
)
Здесь мы создаем для схемы "типы" словарь с допустимыми значениями.

А в таблице для сохранения сущности создадим поле размера:
size types.size_enum NOT NULL

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

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

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

Именно поэтому, многие разработчики отказываются от ENUM поля и просто делают его VARCHAR, так как это дает больше гибкости и возможности к масштабируемости. Но тогда ответственность за целостность данных в БД ложится на плечи разработчика и самой программы.

Мне кажется, что второй вариант более подходящий на проекте, где ТЗ постоянно меняется в процессе разработки.

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

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

Надеюсь, что основная идея понятна и эта статья поможет сделать ваше приложение более простым. Всем успехов, до новой встречи.
1