Войти
 
 
   
 
  
Новости Notes.ру Библиотека Биржа труда Вопрос - ответ Форум Регистрация Поиск О проекте
Разделы
О Notes
Советы
Шаблоны и примеры
Литература
Презентации
 
Всё о задаче AdminP. Часть вторая   Во второй части мы завершаем рассмотрение AdminP. В ней рассмотрены запросы междоменного администрирования и способы управления функциями AdminP с помощью настроек документа сервера, команд консоли сервера, файла Notes.ini и интервалов очистки базы данных. В этой статье предполагается, что вы опытный администратор Domino и прочитали первую часть
О Notes Читать статью
 
Всё о задаче AdminP. Proxy-действия в R5 и Domino 6   Приложение к статье об административном процессе
О Notes Читать статью
 
Всё о задаче AdminP. Часть первая   Перевод классической статьи 2003-его года о задаче административного процесса (AdminP). Очень полезна для понимания работы механизма этой задачи. В первой части статьи описаны компоненты задачи AdminP, как они работают, и как их использование помогает сделать работу администратора Domino проще. Задача AdminP (сакращённо от Administration Process, Административный процесс) работает с базой административных запросов (Administration Requests, admin4.nsf)
О Notes Читать статью
 


О Notes

Главная   Библиотека   О Notes

О пользе ООП при проектировании систем на платформе Lotus Notes/Domino

О пользе ООП при проектировании систем на платформе Lotus Notes/Domino
Василий Соляник

Цель данной статьи - попытаться показать, как можно вести разработку систем на платформе Lotus Notes/Domino с использованием идей и подходов объектно-ориентированного программирования.

Автор ограничил данный материал рассмотрением применения методов ООП для случая разработки систем под «толстого клиента».

Теория

Простота разработки систем на платформе Lotus Notes таит в себе множество подводных камней. Например, во многих местах можно использовать либо скрипт, либо @-формулы. И однозначно сказать, что лучше использовать, вряд ли представляется возможным. Так как все очень сильно зависит от специфики разрабатываемого приложения. Но тем не менее хочу представить на Ваш суд некие правила и попытаться показать, какие плюсы можно получить следуя им.

Правило 1. Визуальное представление и логика должны быть разделены (и, мне кажется, Lotus это позволяет сделать, в большинстве случаев, очень даже просто и удобно)

Правило 2. Использовать ООП подход в написании кода (т.е. использовать классы не ради классов, а использовать именно методы и принципы ООП)

Из правила 1 следует некий (прямо скажем, неоднозначный) вывод: если в элементе дизайна есть выбор между использованием скрипта или @-формул, то чаще всего предпочтение будет отдано скрипту (в более жестком звучании – всегда скрипту)

Давайте рассмотрим более подробно эти правила. Начнем с первого.

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

Что мы используем при разработке в среде Lotus Notes для отображения информации для пользователя:

  • представления (папки) – формулы отбора, формулы столбцов – все только @-формулы
  • формы (подформы) – в полях для отображения так же используются @-формулы.

Т.е. как бы сам Lotus толкает нас использовать для отображения информации @-формулы.

А вот когда нам эту информацию нужно изменить...

И вот тут предлагается ввести жесткое правило: для изменения информации (т.е. для реализации бизнес-логики) всегда и везде использовать скрипт!

Вставляем в форму кнопку – вешаем на нее скрипт. Используем действие в представлении – вешаем на него скрипт.

Конечно, Вы можете возразить: «на кой мне писать кучу скрипта, если мне нужно в этой кнопке повесить действие, которое должно поставить в это поле это вот значение?!»

А вот оно! Сам вопрос получается отвязанным от контекста. Зачем нужно менять именно в этом поле на именно это значение? Наверняка ж это некое звено в некой цепочке бизнес-логики. И когда мы начнем описывать эту цепочку подробнее, то окажется, что нужно не только поле изменить, а потом прописать информацию в историю изменения документа, документ сохранить и послать письмо…

Даже если эти действия планируется делать не при нажатии этой кнопки, все равно эту цепочку лучше держать в одном месте.

Против контраргумента, что скрипт выполняется значительно медленнее, нежели формулы, можно сказать следующее. Когда пользователь ищет, просматривает, документы – время отображения критично (и @-формулы дают выигрыш). А когда пользователь нашел, выбрал, что хотел, и начинает выполнять действия над выбранным документом (документами), то время выполнения действия чаще всего менее критично.

Против контраргумента: "библиотеки не использую, т.к. глюки с перекомпиляцией и повторным использованием, при изменениях в базовых библиотеках" – можно сказать: да, упомянутые проблемы есть. Но их преодоление - тупая техническая процедура, которую можно автоматизировать или поручить новичку, и она не сравнима по сложности ковырянии в "кодослизи" (удачный, на мой взгляд термин, придуманный Дмитрием Акуловым) по всему приложению.

Надеюсь, мне удалось более-менее показать, что разделение логики и отображение не просто возможно в Lotus, но и осуществимо.

Теперь о втором правиле. Скорее это не правило, а подход. Давайте попытаемся применить несколько паттернов.

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

Для преодоления этой проблемы, как мне кажется, очень полезно будет использовать паттерн Wrapper (Adapter ) – создать класс-обертку, который сокроет от тебя (и от твоих товарищей) название полей. Будут существовать лишь публичные методы, посредством которых и будет происходить изменения значений в самом документе. И теперь мы будем оперировать не значением в неком поле и названием поля, а методами определенного класса.

Примечание. Чаще всего Wrapper’ы будут организованы в иерархию. И здесь, возможно, пригодиться паттерн Composite [1].

Теперь в действиях (грубо говоря – в кнопках) мы получаем текущий документ, обворачиваем его в подходящий класс-обертку (для этой цели можно использовать, например, паттерн Abstract Factory [1]) и пишем логику обработки текущего документа используя публичные методы этого класса.

В дальнейшем для удобства лучше вынести весь код в библиотеки. И теперь в кнопках красуется одна, две строки (здесь уместен паттерн Fasade – более на нем пока не будем останавливаться)

Таким образом, у нас вся логика все более и более концентрируется в библиотеках.

Классы-обертки – это, как бы, пассивная часть логики. Они лишь отделяют программиста от «общения» с полями напрямую. Теперь рассмотрим активную часть логики - тот самый код, который мы пишем в действиях (в кнопках). И вот этот код лучше всего «держать» в отдельных классах. Для каждого обособленного действия (например «подписать приказ», «отказать», «вернуть на черновик») мы создаем отдельный класс. И это уже паттерн Command

Ну а если в нашем приложении обертки будут организованы в иерархию, то лучше всего сгруппировать паттерн Command с паттерном Visitor. Это позволит нам избежать вниз сходящих (опасных) преобразований. Возможно, выгода от такого симбиоза сразу не совсем очевидна. Особо хочу обратить Ваше внимание на классы-действия и использование их в коде для кнопок.

Пример.

Пусть у нас есть база «Справочник организации» (база, где хранятся иерархия организации, данные о сотрудниках и прочая кадровая информация).

Какие в нем есть объекты: сотрудник, подразделение, организация.

Какие действия нам нужны: ну, скажем, создание, изменение, перемещение.

'------------------------------------------------------
'--- класс, который будет то, что в паттернах называется AbstractFactory
' он обвертывает документы в подходящие обертки-классы
'------------------------------------------------------
Class StaffDatabase
' метод возвращает корень иерархии. Именно по этому полезен использовать симбиоз Command & Visitor
' в примере используется определения подходящего паттерна по значению в поле Form, но это конечно не требование

Public Function wrap( xDoc As NotesDocument ) As StaffNode
If xDoc.Form(0) = "Person" Then Set wrap = New StaffPerson( xDoc )
'….
End Function
End Class


'------------------------------------------------------
' --- этот визитер будет корнем для иерархии классов-действий
'------------------------------------------------------
Class StaffVisitor
Public Function visitPerson( xNode As StaffPerson )
End Function
Public Function visitDepartment( xNode As StaffDepartment )
End Function
End Class


'------------------------------------------------------
' --- класс, корневой для иерархии классов-оберток для нашей системы
'------------------------------------------------------
Class StaffNode
Private meDoc As NotesDocument
Sub New( xDoc As NotesDocument )
Set meDoc = xDoc
End Sub

Public Function setName( xVal As String )
End Function
Public Function setParent( xNode As StaffDepartment )
End Function
Public Function getParent() As StaffDepartment
End Function
Public Function accept( xVis As StaffVisitor )
End Function
End Class

'------------------------------------------------------
'--- класс-обертка для документа-сотрудника
'------------------------------------------------------
Class StaffPerson As StaffNode
Sub New( xDoc As NotesDocument )
End Sub
Public Function setName( xVal As String )
meDoc.PersonName = xVal
End Function
Public Function accept( xVis As StaffVisitor ) 'в соотвествии с использование паттерна Visitor переопределям этот метод в каждом классе
Call xVis.visitPerson( Me )
End Function
End Class

'------------------------------------------------------
'--- ну а этот класс соотвественно для документа-подразделения
'------------------------------------------------------
Class StaffDepartment As StaffNode
Sub New( xDoc As NotesDocument )
End Sub
Public Function setName( xVal As String )
meDoc.UnitName = xVal 'сравните аналогичный метод в классе StaffPerson
End Function
Public Function accept( xVis As StaffVisitor )
Call xVis.visitDepartment( Me )
End Function
End Class

'------------------------------------------------------
'--- класс-действие
' редактирование
'------------------------------------------------------
Class StaffActionEdit As StaffVisitor
Public Function visitPerson( xNode As StaffPerson )
'здесь можно вызвать соотвествующий инструмент для редактирования
'документа-сотрудник (ну модальное окно например)
End Function
Public Function visitDepartment( xNode As StaffDepartment )
'ну а здесь произвести свои действия, которые именно касаются редактирования
' документа-подразделения
End Function
End Class


'------------------------------------------------------
' Код для кнопки «Редактировать» код будет выгрядить так
'------------------------------------------------------
Sub Click(Source As Button)
Dim ws As New NotesUIWorkspace
Dim doc As NotesDocument
Set doc = ws.currentDocument.Document
' кстати, и код выше так же можно вынести в классы...

Dim db As New StaffDatabase(), node As StaffNode, act As New StaffActionEdit()
Set node = db.wrap( doc )
Call node.accept( act )
' и этот код можно вынести, используя паттерн Fasade
End Sub

Теперь, предположим, нам необходимо выполнить действие «Создание карточки сотрудника». Это действие, скорее всего, мы поместим в представление, так как мы б хотели заносить нового сотрудника в то подразделение, на котором стоит курсор. Но курсор может стоять как на документе-подразделении, так и на документе-сотруднике, то мы хотим чтобы выполнялось следующии условия:

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

Итак, создаем класс-действие:
'------------------------------------------------------
'--- класс-действие
' Создание карточки сотрудника
'------------------------------------------------------
Class StaffActionNewPerson As StaffVisitor
Public Function visitPerson( xNode As StaffPerson )
Dim par As StaffDepartment
Set par = xNode.getParent()
Call par.accept( Me )
End Function
Public Function visitDepartment( xNode As StaffDepartment )
'Здесь размещается код создания нового документа-сотрудника
'и определение этого документа ответным к документу в xNode
End Function
End Class


'------------------------------------------------------
' Код для кнопки «Новый сотрудник» код будет выгрядить так
'------------------------------------------------------
Sub Click(Source As Button)
Dim s As New NotesSession
Dim doc As NotesDocument
Set doc = s.CurrentDatabase.UnprocessedDocuments.GetFirstDocument()
' здесь немного поменялось в сравнении с тем же куском кода для кнопки редактирования

Dim db As New StaffDatabase(), node As StaffNode, act As New StaffActionNewPerson()
Set node = db.wrap( doc )
Call node.accept( act )
' а здесь изменился только класс Action
End Sub

К довершению к коду приведем схему классов примера:


Вывод.

В итоге мы имеем:

  1. Для каждого объекта в системе мы создаем класс-обертку.
  2. Для получении этих оберток мы будем использовать класс-фабрику (который будем отделять нас от NotesDatabase, т.е. как бы являться надстройкой, или даже можно сказать оберткой над ним)
  3. Для выполнения действий над объектами системы мы будем создавать классы-действия, наследуя их от Visitor’а. (т.е. на каждое действие будет приходится по одному классу)

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

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

Заключение.

Тема ООП и Лотус, конечно, более обширна, и в этой статье я лишь только коснулся ее. За «бортом» остались такие, на мой взгляд, интересные вещи, как:

  • использование MVC (хотя обычно его не выделяют как отдельный паттерн) в модальных окнах.
  • использование паттерна Adapter не только как обертку для документов, но и по его «прямому» назначению. А его приходится очень часто использовать, т.к. в лотус-скрипте отсутствует множественное наследование
  • особенности использование Singleton
  • ну и примеры использование других паттернов и их симбиоза.

Благодарности

Ну и в заключении мне хочется выразить благодарность:

Акулову Дмитрию, Каретникову Николаю, Голембиовскому Андрею за огромную помощь в написании этой статьи.

Литература.

(Приведенные ниже ссылки на книге не реклама. Это просто первые ссылки при поиске в google.com по фамилиям авторов или по названиям книг).

[1] Приемы объектно-ориентированного проектирования. Паттерны проектирования. Гамма, Хелм, Джонсон, Влиссидес. http://www.books.ru/shop/books/8451
[2] Применение шаблонов проектирования. Дополнительные штрихи. Джон Влиссидес. http://www.williamspublishing.com/Books/5-8459-0393-9.html
[3] Применение шаблонов Java. Стивен Стелтинг, Олав Маассен. http://epokupka.ru/tov/primenenie-shablonov-java-biblioteka-professionala-_1
[4] Философия Java. Б. Эккель. http://www.javable.com/docs/books/piterpress/thinking_java/

Материалы по теме на Notesnet.ru
Пример UI элемента на основе паттерна MVC >>>
Использование паттерна MVC в модальных диалоговых окнах >>>
Паттерны. Практикум программирования >>>
 
  Опубликовано — 10/02/2006 |    

Василий (автор), 06.10.2006:
> При разработке приложений для внешнего заказчика этот подход использовать нельзя.
Причины:
> 1. Время и соответственно стоимость разработки приложения увеличивается.
Вопрос настолько спорный... Даже не знаю как Вы мерили время :-)
По личным наблюдениям: возможно времени нужно несколько больше на начальном этапе, но при модификации, расширении функционала - тратиться намного меньше времени и сил.
> 2. Ошибок при разработке таким методом будет однозначно больше. При тестировании заказчиком это, конечно же, будет отмечено.
> 3. Время на устранение ошибок уйдет больше.
> 4. Поддержка такого приложения много сложнее. Зачастую заказчик требует срочного устранения ошибки. При таком подходе это затруднено.
Опять же - почему? И опять же по личным наблюдениям: ошибок как раз становиться не просто меньше, но их еще и проще отловить. Приложение становится как бы более предсказуемым. Поддержка - проще за счет сосредочение логики в одном месте, за счет упрощение документации (!), за счет разделении логики от оберток.
> 5. Документация по такому приложению сложнее.
Не вижу причин, почему документация становится сложнее? Мне думается что как раз наоборот. Не нужно описывать в подробностях каждый класс, достаточно написать назначание класса и его роль в тех паттернах, в каких он используется.
> Вообщем, проблемы будут расти как снежный ком. Чем дальше, тем больше.
Может я что не понял, но все те минусы, о которых Вы говорите, есть минусы как раз подхода не ООП :-)
> То что хорошо при использовании Микрософтовских технологий не хорошо при использовании Нотусовых.
ООП в целом и паттерны в частности - не есть микрософтовские технологии. Или Вы о чем другом?

Михал, 05.10.2006:
1. Ну не заточен Lotus Notes под использование такого подхода: постоянные проблемы с перекомпиляцией, частый отказ работы дебаггера, плохая читабельность классов написанных в Declarations в отличие от стандартных подпрограмм и функций и тд.
-----------------------------------
Есть ScriptBrowser от TeamStudio. Помогает и оч. сильно. Проблемы с перекомпиляцией не возникало. Сам пользую нечто подобное. Написал - нажал рекомпайл - и пошёл пить кофе.
2. Для написания эффективной системы в среде Lotus Notes недостаточно использовать только LotusSrcipt. Необходимо знать и использовать формулы, команды и скрипт.
------------------------------
Логику процессов надо переносить на Лотуссрипт. Никто ведь не писал "Формулы долой!". Нет, не долой. Но использовать только там, куда без скрипта не сунешься.

Юрий, 04.10.2006:
Данный подход в Lotus Notes можно использовать для написания систем для собственной компании и предполагая, что этот же разработчик далее будет ее же (систему) поддерживать. Но я бы хорошо подумал...
При разработке приложений для внешнего заказчика этот подход использовать нельзя. Причины:
1. Время и соответственно стоимость разработки приложения увеличивается.
2. Ошибок при разработке таким методом будет однозначно больше. При тестировании заказчиком это, конечно же, будет отмечено.
3. Время на устранение ошибок уйдет больше.
4. Поддержка такого приложения много сложнее. Зачастую заказчик требует срочного устранения ошибки. При таком подходе это затруднено.
5. Документация по такому приложению сложнее. Вообщем, проблемы будут расти как снежный ком. Чем дальше, тем больше.
В результате, если заказчик не уйдет к другой фирме-разработчику после прочтения оценки времени на разработку (у фирмы использующей традиционный подход оно будет меньше), то он скорее всего в дальнейшем откажется от услуг.
Нередко некая база данных (незнакомая для вас) отдается заказчиком на доработку. И в 99,9% (работаю в этой области более 8 лет) без какой либо документации по коду.
И если она написана с использованием такого подхода - то это намного увеличивает время, необходимое для ее доработки. Потому как разобраться в таких вот хитросплетениях классов очень тяжело.
То есть возвращаемся к тому же. Если заказчик разбирается в нотусах (а такое бывает нередко) - то он не оценит такой вот стиль написания.
То что хорошо при использовании Микрософтовских технологий не хорошо при использовании Нотусовых.
Ну не заточен Lotus Notes под использование такого подхода: постоянные проблемы с перекомпиляцией, частый отказ работы дебаггера, плохая читабельность классов, написанных в Declarations в отличие от стандартных подпрограмм и функций и тд. Для написания эффективной системы в среде Lotus Notes недостаточно использовать только LotusScript. Необходимо знать и использовать формулы, команды и скрипт.
А если приложение также должно работать и под web, то это просто необходимо. Плюс еще много чего.
Юра.

сергей, 04.10.2006:
Все красиво, только для распределенных систем такой подход нерационален, прежде всего из-за ошибок периодически возникающих при обновлении библиотек. Хорошо если чтото сломалось, когда сервер стоит рядом, а если он во Владивостоке? Как будем перекомпилировать скрипты? Где будем набирать "новичков" для тупой технической работы и кто это будет в итоге оплачивать? Да и нет в LN конструктора классов как такового, если нравиться продукция MS пищите Win приложения, зачем смешивать все вместе? А как быть с LN приложениями под WEB? там то какие классы можно конструировать... В общем довольно спорная статья на мой взгляд...



Добавить комментарий
Имя * :
e-mail
Комментарий * :
Код подтверждения * :


Мероприятия
Пресс-релизы
Биржа труда
Последнее на форуме
 
А так же:
Как удалить профиль?
16.04.2016 00:08:51
Скопировать в буфер поле документа
24.05.2015 08:55:52
Импорт DXL-описания документов в Lotus Domino. Одноимённые поля
16.04.2015 16:49:58
 
© LOGOSPHERE.RU