Общей особенностью программных систем является возможность пользователям определять свои собственные поля в структурах данных. Рассмотрим адресную книгу — есть множество вещей, которые вы можете добавить. С появлением новых социальных сетей каждый день пользователи могут захотеть добавить новое поле для идентификатора Bunglr в свои контакты.
Для целей в памяти часто лучший способ сделать это — позволить классам включать поле hashmap для пользовательских полей (шаблон, который Кент Бек вызывает Variable State ).
# ruby class Contact attr_accessor :firstname, :lastname def initialize @data = {} end def [] arg return @data end def []= key, value @data[key] = value end end aCustomer = Contact.new aCustomer.firstname = "Martin" aCustomer[:bunglrId] = 'fowl'
При такой настройке вы можете добавить средства в свой пользовательский интерфейс, чтобы пользователи могли присоединять новые поля к объектам. Если вам нужны общие пользовательские поля, вы можете использовать переменную класса, чтобы сохранить список общих ключей для хэш-карты. Существует некоторая неловкость в том, что к обычным полям обращаются иначе, чем к пользовательским полям, но в зависимости от вашего языка даже это можно преодолеть. Если ваш язык поддерживает [a href = «http://martinfowler.com/dslCatalog/dynamicReception.html» style = «font-size: 16px; цвет фона: прозрачный; цвет: rgb (130, 55, 151); текст -decoration: none; «] Dynamic Reception, затем вы используете его для доступа к хэш-карте с обычным доступом к полям.
class Contact... def method_missing(meth, *args) if @data.has_key? meth return @data[meth] else super end end
Часто самой сложной частью этого является выяснение, как сохранить это. Если вы используете базу данных без схемы, то обычно это просто — вы просто добавляете определяемые пользователем ключи к определенным в вашем приложении. Хитрость исходит из базы данных со схемой хранения , особенно реляционной базы данных.
Обычно лучшим вариантом является использование Serialized LOB , по существу создавая большой текстовый столбец, в котором вы сохраняете пользовательские поля в виде документа JSON или XML. В настоящее время многие базы данных предлагают довольно хорошую поддержку для этого подхода, включая поддержку индексации и запросов на основе структуры данных в LOB. Однако такая поддержка, если она доступна, обычно более неудобна, чем использование полей. [1]
Другой маршрут использует какую-то таблицу атрибутов . Стол может выглядеть примерно так.
CREATE TABLE ContactAttributes ( contactId INTEGER, attribute TEXT, value TEXT, PRIMARY KEY (contactId, attribute))
Опять же, запросы и индексация неудобны. Запросы могут включать в себя множество дополнительных объединений, которые могут быть довольно запутанными.
Заранее определенные пользовательские поля предлагают другую систему. Здесь вы устанавливаете схему с полями вроде custom_field_1
(и, возможно,. custom_field_1_name
Вы ограничены только числом настраиваемых полей для каждого экземпляра, которые вы предварительно определили. Как обычно, индексирование и запросы неудобны.
При использовании таблицы атрибутов или предварительно определенных настраиваемых полей вы можете выбрать разные столбцы для разных типов данных SQL. Таким образом, предопределенные поля могут быть integer_1, integer_2, text_1…
, или таблица атрибутов может иметь несколько полей значений ( text_value, integer_value
).
Динамическая схема представляет собой подход , который часто упускается из вида. Для этого вы настраиваете все так, чтобы при добавлении поля вы использовали alter table
оператор для добавления этого поля в таблицу. Наша команда Mingle делает это и была довольна тем, как все получилось. [2] Новые поля могут быть проиндексированы и опрошены так же, как поля, определенные приложением. Это означает, что все экземпляры получают все поля, поэтому это менее удобно, если вы получаете большое расхождение между экземплярами.
Ваш выбор схемы устойчивости будет зависеть от того, что вы используете для реляционного отображения. Определяемые пользователем поля — не самая удачная часть проблемы реляционного отображения, поэтому поддержка различных библиотек реляционного отображения сильно варьируется.
Определяемые пользователем поля аналогичны неравномерным типам [3] . Обе проблемы приводят к необходимости более гибкой схемы или действительно безсхемного подхода (хотя помните, что отсутствие схемы не означает, что у вас нет схемы ). Если у вас есть неоднородные типы, которые не изменяются по желанию пользователя, тогда может иметь смысл один из шаблонов, ориентированных на наследование. ( Одной таблицы наследования , класс Таблица наследования или бетона Таблица наследования .)
Заметки
1: Брет Тейлор описывает схему индексации полей в такой схеме, создавая отдельные таблицы индекса для каждого индексируемого поля.
2: подход Mingle на самом деле немного сложнее, чем просто добавление полей в существующую таблицу. Центральный тип записи Мингла — это карта (которая представляет истории, задачи и т. Д.). Поля на карте варьируются в зависимости от проекта, и вы можете иметь много проектов в одной базе данных. Поэтому вместо того, чтобы использовать один карточный стол, mingle создает новую таблицу для каждой карточки проекта. Затем он динамически добавляет поля в эту таблицу по желанию пользователя.
3: Неоднородные типы — это типы, в которых экземпляры используют небольшой и очень различный выбор полей. Иногда они называются разреженными таблицами, потому что если вы посмотрите на всю таблицу, каждая строка использует только небольшое количество большого списка столбцов. Различие между неоднородными типами и пользовательскими полями состоит в том, что неоднородные типы имеют все потенциальные поля, известные разработчикам, в то время как пользовательские поля позволяют создавать поля, о которых разработчики никогда не узнают.