PonyORM: наследование - это подходящий выбор дизайна? - PullRequest
0 голосов
/ 27 марта 2020

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

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

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

class TagOwner(db.Entity):

    tags = Set("Tag")

class Tag(db.Entity):

    name = Required(str)
    owners = Set("TagOwner")

И тогда вы унаследуете сущности, которые должны иметь теги от TagOwner:

class Room(TagOwner):

    pass

class Character(TagOwner):

    pass

И так далее. Пони может справиться с этим дизайном просто отлично. Но как? Он сгруппирует сущности Room и Character в одну таблицу со всеми их сгруппированными столбцами. Комнаты и персонажи теперь хранятся вместе. Это необходимо, поскольку тег должен иметь внешний ключ к объекту в обеих сущностях. Теоретически, наличие обеих сущностей в одной таблице не является проблемой: pony будет обеспечивать разделение нужных типов объектов при запросе, и дополнительные столбцы в каждой строке также не будут иметь большого веса. Но это теория.

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

Итак, зачем все-таки беспокоиться об этом TagOwner? Давайте просто бросим это! Мы могли бы добавить каждый tags = Set("Tag") к комнате, персонажу, транспортному средству и тому подобное. Беспокойство, просто строка, верно?

Но ... вам также нужно указать что-то в сущности Tag. Если вы не ссылаетесь на класс TagOwner, то что? Насколько я знаю, вы не можете написать что-то вроде:

class Tag(db.Entity):

    name = Required(str)
    owners = Set("Room", "Character", "Vehicle", "Object", ...)

... и это кажется вполне логичным в конце концов. Как и я, вы можете подумать об удалении owners в целом (в конце концов, я буду делать конкретный запрос c каждый раз, когда мне придется искать, большое дело). Кроме того, опять же, если вы используете Set("Tag") на одной стороне, вы должны иметь Set(entity) обратно в класс Tag. Pony не предлагает системы Many2Many или Many2One, в которой определяется только одна сторона отношения.

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

Наследование ... композиция ... что-то еще вообще? Единственное решение, которое я вижу, чтобы избежать проблемы наследования, - это полное удаление отношения, сохранение идентификаторов тегов или имен в IntArray или StrArray или в чем-то подобном, но это определенно не очень хорошо.

Предложения наиболее Добро пожаловать!

Спасибо,

...