Pull to refresh

ООБД без ООП

Reading time6 min
Views6K
Лично мне не надо объяснять, что такое ООП. Я сам в первую очередь мыслю существительными и только во вторую — глаголами.
Но речь не о том, кто как мыслит; я хочу обсудить ситуацию, когда отказ от привычных механизмов ООП упрощает работу с объектами.

Как, пример, можно вспомнить добрым словом Lotus Notes, где имя формы хранилось внутри документа. Создавая форму LN, мы тем самым описываем новый UI класс, в котором можно добавлять свойства и переопределять методы (Queryopen, Postsave и пр.). При этом новый объект, созданный с помощью этой формы, не связан с ней механизмом наследования. Форма – это свойство объекта, и в LN есть команда «SwitchForm», с помощью которой можно открыть объект с другой формой, естественно, с вызовом других методов. Неопределенные свойства при этом вернут пустую строку.

Есть некий терминологический туман в части документо-ориентированных и объектно-ориентированных БД. Считается, что ДОБД это NoSql, а в ООБД должны храниться методы классов. Однако тот же LN хранил в базе описания форм и прочий дизайн и при этом считался ДОБД, которую незадолго до смерти портировал в реляционную DB2.

Надеюсь, туман не усилится, если я буду называть объекты, хранимые в ООБД, документами, а их свойства – полями.

Пример с формой легко реализуется в ООП:

Создаем новый класс, унаследованный от базового класса FORM, переопределяем несколько свойств и методов. Новый документ связан с конкретной формой, если создается пользователем из фронтенда. Если же он создается программно, свойство FORM может остаться пустым, в этом случае при открытии будет использоваться форма по умолчанию.

Т.е. мы определяем визуальное поведение объекта не за счет добавления методов в класс, а за счет того, что храним в объекте ссылку на форму, в которой эти методы описаны, точнее, храним в документе название класса формы.

У знатоков ОРМ могут возникнуть замечания. Я предлагаю временно забыть про ОРМ, а лучше навсегда.

Идем дальше. Форма – важная часть UI, но приложение имеет и другие классы, с формой никак не связанные. Например, методы и свойства, используемые при создании связанного объекта.

При проектировании СМК объект «Корректирующее воздействие» может создаваться по-разному в зависимости от объекта-основания. Инцидентом-основанием может служить как рекламация о браке, так и письмо Б. Обамы, при этом методы создания нового объекта должна отличаться.

Аналогично и наоборот: связанный объект может воздействовать на объект-основание так, как он считает нужным, как предписывает его классовая сущность.

Реализуем в ООП:

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

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

В результате имеем:

1. Если документ имеет поле LinkRule, он может служить основанием для осмысленного создания других объектов.
Пример: документ имеет поле {'LinkRule': 'Action'}, — связанный документ будет создан так, как велит объект класса "'Action'" (Action унаследован от класса LinkRule).
2. Предположим, документ имеет поле {'LinkRule': ['Action', 'Run']}. В этом случае приложение может вызвать два метода для инициализации связанного объекта (композитное создание – звучит необычно), а может спросить у пользователя, чего он хочет.
3. В процессе работы у объекта может измениться поле {'LinkRule': ['Action', 'Run']} на {'LinkRule': ['Action', 'Stop']}, соответственно изменится классовая принадлежность. Иногда и люди пол меняют. Отправили, к примеру, исходящий документ из МинФина в МинОбр, он попал в БД МинОбра и стал входящим. Форма осталась старая (поля «Подписал», «Визы», «Исполнитель» и пр. остались), а повадки должны смениться. Почтовый агент должен заменить у объекта правила игры так, как прописано в классе, указанном в поле «MailRule» этого объекта. Почтовый агент тоже играет по правилам, берущим начало в базовом классе MailRule.

Есть документ, в нем поля:

{
'UUID' : '301D8348DEE65E9B3C5C5D3DF9864CF2',
'Form' : 'Bullgrader',
'LinkRule' : ['Action', 'Run'],
'ResponseRule' : 'Eight8',
'MailRule' : 'Mailware',
…,
еще много разных полей,
…
}


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

if ( doc.getField("Form") == "Bullgrader" ) 
	uiDoc = new Bullgrader(doc);
// можно сделать и поизящней
…
uiDoc.queryOpen();
…


Все понятно, кроме одного: стала ли БД объектно-ориентированной?

С одной стороны, да. Объекты хранят внутри себя принадлежность к определенному классу и связаны с конкретными методами. С другой стороны, – какая же это ООБД, такие вещи можно сделать в нормализованной РСУБД, а использование таких механизмов в ОРМ концептуальные аналитики вообще назовут датабазовой шизофренией.

Кроме того, в ООБД сами методы должны храниться в базе, а здесь этого нет.

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

Опыт показал, что связанные с документом классы за исключения класса FORM очень простые, они состоят, как правило, из одного конструктора и нескольких свойств.

Заменяем слово КЛАСС на ПРАВИЛО. В базе создаем справочники всех правил:
LinkRules – набор правил для создания связанных объектов;
ResponseRule – набор правил, описывающих воздействия на объекты-основания;
Forms – список описаний форм;
и т.д.

Правила – это совокупность скриптов и констант, но считать их методами документа нельзя. Приложение выполняет скрипт не как метод объекта, а как функцию. При этом сам объект передается в качестве параметра или хранится в переменной с определенным именем.
Правила (как правило) представляют собой небольшие скрипты, лично мне больше нравится хранить их в базе, но есть мнение, что все надо хранить в виде файлов. Можно и так, и так.

Большинство правил представляют собой документы со следующим набором полей:

{
…,
'Form' : 'LinkRule',
'Name' : 'Action',
'File' : 'linkrule_action.py',
'Rule' : "linkedDoc.sendDa = today()\nlinkedDoc.title = 'Действие'"
}


Правила, впрочем, как и другие справочники, есть смысл загружать при старте сервера и хранить в памяти. Это значительно ускоряет работу программиста и немного ускоряет работу приложения (ну займут они все вместе на сервере 10 мб или 100, – мне не жалко).

Для того чтобы создать правило, нужно заполнить форму из 4 полей. Это значит, нужно разработать форму 'LinkRule', сделать фильтр для отбора правил, написать скрипт для их загрузки при старте сервера.

Пример на Python демонстрирует инициализацию связанного документа (linkedDoc) по правилу, заданному в документе-основании (doc). Функция getRule возвращает нужное правило в виде текста, danger(lr) проверяет его на вредоносность, exec выполняет инициализацию.

    lr = getRule( 'linkRules', doc.linkRule )
    if lr and not danger( lr ):
        try:
            exec( lr, globals(), { 'doc' : doc, 'linkedDoc' : linkedDoc } )
        except Exception as ex:
            snd ('setLinkedFields: %s\n%s' % (lr, ex))
            return False


Так как формы для большинства правил отличаются только названием, можно сделать одно описание формы для нескольких правил:

{
…,
'Form' : 'Form',
'Name' : ['LinkRule', 'MailRule', 'ResponseRule'],
…
}


На фильтрах тоже можно сэкономить, сделав категоризованное представление правил.

В одном из проектов в базе хранилось более 200 правил 11 видов. Это много и это говорит о том, что центр тяжести системы сползает в сторону описания правил, т.е. в область настроек. Гибкость увеличивается, а главное, такая архитектура позволяет исполнять капризы заказчика, не трогая исходных кодов инструментальной части системы. При этом изменение и отладка правил не требует даже перезагрузки сервера, достаточно перегрузить справочники.

Правила могут описывать не только поведение объектов, но и состав таблиц, отчетность, кнопки на экране и многое другое. И все это почти без ООП: для работы с правилами в БД должны храниться объекты только одного класса Document.

Все перечисленное – это не теория. Для тех, кто хочет посмотреть и потрогать, я выложил в сеть простенькую бесплатную crm-sova с ДОБД на SQLite. Сова написана, чтобы утешить любимую жену, после того, как она купила очень неудобную CRM и расстроилась. Это не реклама, ссылку не даю, но найти несложно. Сразу предупреждаю: из ИЕ не работает, это свойство фреймворка.

– Уважаемый, но на SQLite нельзя сделать ДОБД.
– Почему вы так считаете?
– Спросите любого, я спросил любого, — он сказал, что нельзя.
– А еще аргументы?
– В википедии так написано.
– Да. С этим трудно спорить.
– До свидания.
– Удачи.
Tags:
Hubs:
0
Comments20

Articles