Модель главного шаблона: унаследовать или нет? - PullRequest
1 голос
/ 04 мая 2009

У меня есть сценарий, который нелегко объяснить, но я уверен, что это очень распространенная проблема. Я приложу все усилия, чтобы проиллюстрировать проблему.

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

Итак, в основном у нас есть опросы, которые имеют одинаковую структуру (коллекции, свойства и т. Д.), Но данные могут быть разными.

Как вы моделируете БД?

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

tbl_Survey (id, name, conducted_on)

Как вы моделируете свои классы?

Моя идея была бы:

Survey {
  Name
  Questions
}

ConductedSurvey : Survey {
  //gets the master according to the name
  GetMaster()
}

Важно: Опрос имеет много связей с другими классами. Если мы решили подкласс. Должны ли все они быть разделены на подклассы (потому что мы скопировали бы данные из мастера для каждого объекта)?

1 Ответ

4 голосов
/ 04 мая 2009

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

Хорошо, вы говорите о прототипе. «Шаблон» опроса является прототипом. Ясно, что если прототип имеет точно такую ​​же структуру, что и экземпляр на основе прототипа, было бы глупо и расточительно создавать совершенно разные таблицы для одной и той же структуры - тем более, когда вы понимаете, что это означает любое изменение в этой структуре должен отражаться в обоих наборах.

Могу ли я добавить столбец, чтобы отличить прототип / шаблон от проведенного обследования? Нет, наверное нет. Вместо этого я бы добавил еще одну таблицу с отношением один-к-одному к корневой таблице опроса. В этой таблице я бы добавил немного метаданных, которые отличают прототип от не прототипа.

По трем причинам: 1) в любой разумной системе прототипы будут составлять небольшую часть от общего числа обследований. 2) Я часто хотел бы перечислить все прототипы, например, «Мастер создания нового опроса», в котором перечислены варианты прототипов, на которых будет основан новый опрос. И 3) для хранения этого небольшого количества дополнительных данных:

create table survey_prototype (
    id int not null primary key,
    survey_id references survey(id) -- the regular survey table
    wizard_description varchar(80)
    . . . .
);

Теперь я представляю, что в опросе также есть описание, но для прототипа это описание выглядит как «ЗАМЕНИТЕ МЕНЯ, ЭТО ОПИСАНИЕ, КОТОРОЕ УВИДЕТ ПОЛЬЗОВАТЕЛЬ», но описание wizard_description - что-то вроде «Политический опрос прототипа».

Теперь, поскольку любой поиск прототипа / шаблона не имеет возможности вернуть проведенный опрос (поскольку ни один проведенный опрос не присоединяется к survey_prototype), ваш getMaster выглядит (концептуально, предположительно, вы используете ORM) следующим образом:

ConductedSurvey : Survey {
  //gets the master according to the name
  GetMaster() { "select * from survey_prototype join survey..."
}

Важно: опрос имеет много связей с другими классами. Если мы решили подкласс. Должны ли все они быть разделены на подклассы (потому что мы скопировали бы данные из master для каждого объекта)?

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

Конечно, вам придется принимать это решение на каждом уровне иерархии; было бы неплохо инкапсулировать каждую политику преобразования для преобразования с глубоким копированием в один класс. Шаблон посетителя сделает это, так как он имеет одну перегруженную функцию visit для (базового) класса ваших типов: так (по крайней мере) visitSurvey, visitQuestion, visitAnswer.

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

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

Итак, подведем итоги: таблица survey_prototype, получите прототип, nhibernate извлечет все дерево, когда вы запросите корень в survey_prototype, посетите это дерево с преобразователем глубокого копирования, который возвращает скопированный корень, и пусть пользователь напишет об этом , сохраните скопированный корень и пусть nhibernate рекурсивно сохранит каждый узел дерева. Когда пользователь должен увидеть опрос, не относящийся к прототипу, извлеките корень из опроса с помощью nhibernate, используйте экранный посетитель для его отображения и т. Д.

...