Автор Николай Озниев,
Опубликовано в журнале Программист, №7, 2002г.
Взято с сайта Королевства Delphi: http://www.delphikingdom.com/asp/viewitem.asp?catalogid=880
Приложение со свойствами платформы
Что такое платформа

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

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

Любое прикладное программное обеспечение может быть создано при наличии базовых средств:

  1. инструментальное средство (в нашем случае Delphi);
  2. система управления базами данных (при использовании локальных файлов баз данных может отсутствовать);
  3. конструктор баз данных, в простейшем случае это могут быть утилиты для создания и модификации структур баз данных из поставки Delphi.

К этим средствам надо добавить квалифицированного программиста, и любой бизнес можно обеспечить необходимым прикладным программным обеспечением.

Перечисленный набор вполне можно назвать платформой. При любых изменениях «правил игры» программист сделает необходимые изменения в прикладных программах. Такая платформа – крайность. Не всем по плечу держать в штате программиста (а то и подразделение) для решения эпизодических проблем.

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

Между этими двумя крайностями существует множество промежуточных уровней, когда в программное обеспечение вводятся различные средства, позволяющие адаптировать ПО без переделки кода. Нетрудно понять, что при таких условиях дать четкое понятие платформы крайне нелегко. Впрочем, можно обойтись без этого определения, т.к. каждая фирма, занимающаяся разработкой программ, может для себя определить, каким уровнем сервиса должны обладать ее продукты.

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

В этой области доминируют факторы, характерные для стадии становления систем автоматизации:

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

До недавнего времени разработка платформ было уделом достаточно сильных фирм, в штате которых не один десяток программистов. Однако ситуация уже изменяется, и современные средства разработки, подобные Delphi, вполне подходят для создания гибких прикладных систем при небольших затратах

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

Речь пойдет именно о такой платформе. Но сначала о том, как это происходит на практике.

Разговор с директором перед получением задания

Директор пригласил программиста в кабинет и показал, как можно настроить Яндекс под свои потребности, выбирая из набора списков понравившиеся ему квадратики, где отображались графики изменения курса валют, списки новостей на интересующие именно его темы и другие элементы отображения разнообразной информации. Все это легко перетаскивалось по поверхности интернет-браузера мышью и выстраивалось как пожелаешь. При повторном включении браузера все оставалось так, как только что было настроено. Закончил он это представление словами: «Хочу, чтобы в будущей системе все было так же». Речь шла о платформе для функциональных задач, охватывающих лечебный процесс, начиная с прихода пациента в регистратуру поликлиники, последующим его обслуживанием в поликлинике, лечением в стационаре, и, наконец, выпиской.

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

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

Принципы построения платформы

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

Конструктор баз данных

Трудно представить себе внесение изменений в структуру данных в процессе выполнения приложением своих целевых задач. Отсюда вытекает первый принцип: платформа должна иметь как минимум два режима запуска, один из которых предназначен для изменений структур данных с выключенными функциями, а второй – для решения целевых задач, когда запрещены любые изменения в структурах данных. Назовем эти режимы режимом конфигуратора и пользовательским режимом.

Второй принцип - адаптация структур данных к предметной области. Пользователь должен без труда вносить в систему понятия из своей предметной области. Например, должна быть возможность указать, что в данной таблице хранится информация о врачах, причем ему лучше не видеть идентификатора таблицы вовсе. Проще говоря, каждый атрибут в базе данных должен сопровождаться дополнительной информацией или паспортом, куда пользователь «вписывает» понятные ему категории.

Программно это можно реализовать в виде структур, например:

  // Структура для хранения реквизитов поля
TFieldInfo = record
  sFieldAttr : TStrings; // атрибуты поля:
  { sFieldName    - Имя поля     }
  { sFieldCaption - Наименование }
  { sFieldDescr   - Описание     }
  ...
end;
// Структура для хранения реквизитов таблицы
TTableInfo  = record
  sTableAttr : TStrings; // атрибуты таблицы:
  { sDatabaseName - имя БД       }
  { sTableName    - имя таблицы  }
  { sTableCaption - наименование }
  { sTableDescr   - описание     }
  ...
end;

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

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

Третий принцип - баланс между универсальностью и функциональной достаточностью. Невозможно создать универсальную платформу, ибо тогда она выродится в набор средств, описанный в начале статьи. Поэтому нужны способы разумного ограничения возможностей конструктора баз данных, не ущемляющие его в конкретной предметной области. Один из путей - ввод в конструктор ряда конкретных пользовательских таблиц, а также ряда предопределенных имен полей, в частности для хранения фамилии, имени и отчества. Это существенно облегчает задачу построения конструктора и, что еще важнее, упрощает задачу реализации бизнес-логики.

Четвертый принцип – принцип допустимых ограничений. Без заметных ограничений в реализации таких категорий, как хранимые процедуры, связи между таблицами, ограничений в построении SQL-запросов невозможно обойтись. Например, в платформе, описываемой здесь, используется MS SQL Server, но в пользовательской базе данных пока нет хранимых процедур. Принцип ограничений обусловлен, прежде всего, наличием достаточных ресурсов у разработчика платформы. Чем большими ресурсами располагает разработчик, тем меньшими будут эти ограничения. Замечательно то, что даже силами одного программиста степень ограничений можно свести к минимуму, позволяя создавать довольно сложные бизнес-логики. Нельзя повторить язык SQL в прикладной системе – это неподъемная задача, но можно построить средства для создания очень сложных SQL?запросов типа SELECT, запросов типа INSERT INTO SELECT FROM и других типов. Они позволят делать, скажем 80% работы, если за 100% принять то, что может СУБД в конкретной задаче (о числах можно спорить). В некоторых задачах такие ограничения могут быть недопустимы – все зависит от предметной области. Жизнь такова, что даже такие урезанные конструкторы стремительно увеличивают адаптируемость программ, а потому по этому пути можно и нужно идти. Но ежели у фирмы есть команда из 50 программистов, то ей и на эти ограничения идти не придется. Фирма 1С так и делает, хотя в программных модулях тексты запросов у нее довольно дико выглядят.

Управление функциональностью

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

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

АРМ, т.е. автоматизированное рабочее место. Этот атрибут носит смысл вывески на фасаде здания в том смысле, что он используется для обозначения рабочего места. В частности, наименование АРМ выводится в заголовки экранных форм.

Окно. Может быть использовано как самостоятельное окно Windows, и в этом случае включение такого окна в меню означает возможность запуска конкретной, программно-реализованной формы. Другое назначение этого атрибута – служить верхним уровнем меню, содержащим список подменю, предназначенных для решения ряда схожих задач, образующих в совокупности требуемый режим работы АРМ Например, в верхнем пункте меню Прием больного (режим работы) могут быть подменю или подпункты (содержание этого режима): Журнал приема, Амбулаторная карта, История болезни, Направление на госпитализацию.

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

Алгоритм представляет собой атрибут, содержащий список для хранения последовательности функций, которые необходимо выполнить при выборе пункта меню, к которому он прикреплен. Функции алгоритма выполняются строго в той последовательности, как они следуют в его списке. Если в алгоритме нет ни одной функции, то пункт меню, к которому он прикреплен, становится невыполнимым и возникает исключение.

Функция – это последний уровень в иерархии системы управления функциональностью платформы. Она обязательно содержит указатель на программный компонент - форму, процедуру или функцию на языке Object Pascal.

Все перечисленные атрибуты хранятся в памяти в специальных схожих по типу структурах. Например, для хранения реквизитов АРМ используется структура

// Структура АРМ TArm = record sTopInfo : TTopInfo; sOknoPtrL : TList; end;

где TTopInfo – представляет собой структуру

// Структура универсальной шапки TTopInfo = packed record sFbSUType : TFbSUType; // Тип структуры sID : TFbMedID; // Идентификатор sCaption : TFbMedName; // Наименование sDescr : TFbMedDesc; // Описание end;

Данная шапка используется во всех структурах, поэтому в ней есть специальное поле sFbSUType, определяющее тип структуры. Тип структуры - перечислимый тип:

// Тип структуры объекта управления
TFbSUType = (apArmType, apOknoType, apMItemType, apAlgorType, apFuncType, apChannelBox, apNoneType);

Для удобства программирования вводится комбинированная структура, описывающая все объекты в компактном виде и представляющая собой вариантную запись:

// Обобщенная структура объекта управления TFbSUObject = packed record FbSUType : TFbSUType; case TFbSUType of apArmType : (Arm : pTArm); apOknoType : (Okno : pTOkno); apMItemType : (MItem : pTMItem); apAlgorType : (Algor : pTAlgor); apFuncType : (Func : pTFunc); apChannelBox : (); apNoneType : (); end;

В этой структуре pTArm, pTOkno, pTMItem и т.д. – ссылки на соответствующие структуры TArm, TOkno, TMItem и т.д.

Платформа содержит библиотеку функций для работы с приведенными атрибутами. Настройщик имеет возможность ввести новый атрибут, отредактировать существующий или же удалить ненужный. Все изменения фиксируются в системной базе данных АРМ. Окна, меню, алгоритмы и функции – суть элементы, позволяющие конструировать рабочее место. Ограничение имеется только для функций, заключающееся в том, что функцию может породить только программист ядра системы, хотя настройщик может по своему усмотрению в соответствие с предметной областью изменить содержание ее шапки:

// Структура функции TFunc = record sTopInfo : TTopInfo; sFormName : TFbMedFormName; sAddressPtr: Pointer; end;

В поле sFormName записывается имя прикрепленной формы, которая запускается при активизации структуры функции, т.е. в момент выборки функции из списка алгоритма. Если это имя не задано, то должен быть задан указатель sAddressPtr процедуры или функции. Если ни то, ни другое не задано, структура функции теряет свой смысл. При обращении к такой структуре система генерирует исключение.

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

Настройщик создает столько АРМ, сколько ему нужно иметь различных рабочих мест, снабжая каждое из них наименованием и комментарием, смысл которых соответствует предметной области. Фактически, при этом создаются структуры TArm. Настройщик создает и все необходимые структуры других типов, а также редактирует реквизиты структур функций, чтобы они полностью соответствовали области применения. Затем он формирует дерево управления, в каждый узел которого добавляет один из описанных выше атрибутов, соблюдая принятые соглашения. Это дерево сохраняется либо в системной базе данных, либо в локальных файлах конфигурации. При запуске приложения из дерева управления выбирается нужный корневой узел, т.е. АРМ. Таким образом, корневые узлы дерева управления содержат ссылки на АРМ. Затем специальная система запуска формирует главное меню системы выбранного АРМ, которое и определяет его облик. Вид такого дерева управления показан на рис. 1. На рис. 2 показан фрагмент главного окна соответствующего АРМ.

Рисунок 1. В правой части показано дерево управления системы

Рисунок 2. Меню АРМ Логопед при запуске приложения в пользовательском режиме

Управление пользовательским интерфейсом

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

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

Специфика платформы – в непредсказуемости действий пользователя. Поэтому появляется ряд ограничений, не свойственных «жестким» программам. Например, операции ввода, редактирования и удаления данных становятся унифицированными, ибо заранее неизвестно, к какому источнику данных пожелает подключиться пользователь. Поэтому в систему вводится универсальный редактор БД. Он не должен зависеть от того, как пользователь выстроит систему данных, и должен быть пригоден для любой структуры таблицы и запроса. Автор построил его на базе TStringGrid (рис.3.). Так как TSringGrid не совсем хорош для непосредственного отображения текстов большого объема, графики и форматированных дат, пришлось вставлять в него другие компоненты, приведенные в таблице.

Компонент Назначение
TMemo Редактирование текстовых данных
TСomboBox Выбор данных из списков (для полей списочных типов)
TPanel c TImage Работа с полями графического формата
TDateTimePicker Работа с полями типов TDateTime, TDate и TTime

Для остальных типов данных используются непосредственно ячейки TSringGrid. Принцип редактирования состоит в следующем. Выбранная запись из таблицы или запроса переносится в объект TStrinGrid, в котором в зависимости от типов данных при необходимости вставляются приведенные выше объекты. Затем пользователь редактирует информацию в интересующих его полях, т.е. в указанных объектах или в ячейках TStringGrid, когда объектов нет, а в заключение происходит обновление записи в таблице непосредственно в БД. Структуры для таблиц и полей при этом находят непосредственное применение. В частности, заголовки редактируемых колонок берутся из этих структур.

Рис. 3. Редактор БД.

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

Формирование отчетов

Отчеты – непременный атрибут любого прикладного программного продукта. За основу был взят набор компонентов QuickReport. Но и здесь действуют описанные выше принципы, что заставляет вводить определенные соглашения об устройстве интерфейса.

Стандартное рабочее окно содержит компонент TPageControl, в котором количество страниц TTabSheet выбирается пользователем. По умолчанию стандартная страница предназначена для размещения компонента TDBGrid и стандартного набора средств: для открытия БД, ввода, редактирования и удаления выделенных записей. Кроме того, пользователь имеет возможность изменить состав и порядок отображения колонок данных, а также может установить фильтр или же ввести серию дополнительных условий отбора информации. Таким образом удается обеспечить достаточно высокий уровень сервиса, позволяющий подготовить данные для отчета, который выводится в виде таблицы.

Другой разновидностью стандартного отчета является печать страницы TPageControl, на которой размещен ряд компонентов, скажем, TLabel, TEdit и другие. Это может быть, например, обложка амбулаторной карты, талон или еще какой-либо документ. Для распечатки такой страницы создан стандартный механизм одностраничного отчета. Принцип его работы состоит в повторении геометрии расположения объектов в исходном виде при распечатке отчета.

Еще один вид стандартного отчета построен на основе компонента TStringGrid. В каждую ячейку этого компонента может быть помещена любая информация, а вывод отчета стандартизован. Такой отчет может состоять из нескольких страниц.

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

Предметная область может содержать набор стандартных отчетов утвержденной формы. В этом случае в платформу может быть введен двухступенчатый механизм, первой ступенью которого является инструментарий для подготовки данных, а второй – формирование отчетов. Механизм подготовки данных строится на базе унифицированных средств прикрепления полей БД к объектам отображения данных. Если в БД нет нужных полей, содержащих необходимую информацию для включения в нужное поле отчета, то используется SQL-запрос, подготавливающий требуемые данные. Платформа имеет средства, предоставляющие возможность создавать и хранить такие запросы в системной базе данных, причем каждый их таких запросов снабжается необходимым пояснительным текстом.

Как все это работает в целом

Опишем только самые фундаментальные операции, дающие представление о принципах функционирования платформы.

На сервере БД хранятся системные таблицы, содержащие сведения обо всех пользовательских таблицах и их полях, а также информацию о наборе элементов для формирования дерева управления. Вся эта информация при запуске системы в пользовательском режиме грузится в память и остается неизменной на протяжении сеанса работы приложения. При работе с пользовательскими данными используется информация только из структур памяти, в которых содержатся также необходимые компоненты TTable, TQuery и другие, обеспечивающие доступ к данным и передачу их объектам отображения. Дополнительные объекты типа TTable, TQuery и подобные им на формах не применяются.

При выборе пользователем конкретного АРМ автоматически формируется рабочее меню. Дерево управления, на основе которого создается меню приложения, позволяет формировать специальный индекс, уникальный для данного пункта меню (индекс меню). Этот индекс используется для идентификации файлов настроек, что в свою очередь позволяет вести обширную локальную базу данных индивидуальных настроек пользователя. В момент активизации выбранного пользователем пункта меню определяется индекс меню, затем ищутся все файлы настроек, содержащие этот индекс и производится формирование интерфейса с использованием данных из найденных файлов.

В исходных текстах программ содержится встроенная системная БД, подключаемая через INC-файлы.

Наличие этой базы данных обусловлено рядом факторов.

Во-первых, она просто необходима для практической реализации предопределенных структур таблиц и полей. Поставка системы такова, что при ее инсталляции не требуется обычная в таких случаях предварительная настройка сервера и создание структуры данных. БД на сервере создается платформой при первом пуске в режиме конфигуратора. После этого в автоматизированном режиме, т.е. в ходе диалога с пользователем, производится формирование начальной структуры пользовательской БД.

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

К ним относятся:

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