В этом разделе мы опишем гипотетическую, однако достаточно реалистическую программу, на примере которой можно будет проиллюстрировать структуру и применение группы взаимосвязанных пакетов. Рассматриваемая здесь программа находит прямое применение в различных организациях и обычно связана с задачей управления базой данных.
Тип базы данных | Кем управляется |
---|---|
Пенсионные фонды | Банками или трестами |
Счета отдельных вкладчиков | Конторами маклеров |
Записи об отдельных вкладах | Домашним персональным компьютером |
Портфель акций организации по управлению капиталовложениями | Служащими организации по управлению капиталовложениями |
В каждой из этих программ управление базой данных могут осуществлять различные люди, однако с точки зрения структуры программы все приводимые примеры попадают в одну и ту же категорию. Мы остановимся только на одном классе, с тем чтобы при более подробном рассмотрении использовать одну терминологию, Рассмотрим портфель акций организаций по управлению капиталовложениями. Для тех, кто незнаком с организациями подобного рода, мы покажем, что рассматриваемая программа может иметь значительные размеры.
Историческая справка: Национальная ассоциация организаций по управлению капиталовложениями (NAIC) представляет собой объединение нескольких тысяч организаций, имеющих в среднем по пятнадцать членов. По всему миру насчитывается еще несколько тысяч таких клубов. [Сама ассоциация NAIC является членом Мировой федерации ассоциаций организаций по управлению капиталовложениями]. Сотрудники каждой организации обычно собираются вместе раз в месяц, производя обзор портфелей акций, прослушивая отчеты о новых капиталовложениях и принимая решения о закупке или продаже, осуществляемые обычно на следующий день маклером данной организации. Обычно организация хранит в портфеле ограниченное число капиталовложений, поскольку ответственность за контролем над вкладами распределена между членами организации - обычно один или два вклада на каждого члена организации.
Сначала рассмотрим довольно простой пример системы управления капиталовложениями. В дальнейшем мы осуществим модификации, которые повысят функциональные возможности программы. Мы предложим альтернативные методы, использование которых позволит реализовать и продемонстрировать ряд определенных преимуществ, а также и недостатков. Наш исходный пример иллюстрирует программу с единственным пользователем, в качестве которого выступает секретарь-казначей организации. Этот сотрудник выдает запросы к портфелю акций (базе данных) и использует полученную информацию для составления месячного отчета. Помимо этого, он обновляет информацию в портфеле, отражая произведенные в результате ежемесячных встреч операции по закупке и продаже, информация о которых предоставляется ему маклером организации. Далее мы рассмотрим, каким образом сотрудникам организации могут быть предоставлены иные типы доступа к портфелю.
Наша "объектно-ориентированная" методология разработки программы представляет собой стратегию создания программной модели на языке Ада, структура которой отражает модель системы в реальном мире. Компоненты, образующие структуру программы, выбираются, исходя из их соответствия компонентам модели в реальном мире.
В данном случае рассмотрение реальной модели на верхнем уровне достаточно просто. Модель состоит из двух компонент - секретаря-казначея базы данных портфеля акций и базы данных "портфель", которая "реагирует" на выдаваемые секретарем запросы на получение, обновление и инициализацию в ней информации. В соответствующей программной модели секретарь может быть представлен как задача, интерпретирующая индивидуальные запросы и преобразующая их в процедурные обращения к программе, интерпретирующей базу данных портфеля акций. База данных портфеля акций может рассматриваться как совокупность структуры данных и набора процедур управления ею, включающую процедуры создания базы данных, ее инициализации, запроса и обновления. Возможным программным аналогом в языке Ада может служить пакет-владелец (портфель-владелец). Одно из начальных возможных представлений программной структуры показано на рис. 2.1.
Рассматривая структуру на рис. 2.1 как базовую, мы можем указать информацию, которую необходимо поместить в базу данных, а также тот набор запросов, который разрешается выдавать секретарю-казначею. После введения этих требований можно перейти к рассмотрению того представления данных, которое может быть выбрано для структур данных в базе как объектов типа "структура".
Эти условия позволяют нам затем оценить "семантический разрыв", если таковой имеется, между запросами, выдаваемыми секретарем-казначеем, и более примитивными функциями по созданию, обращению и обновлению объектов, полагая, что мы знаем, как описать их на языке программирования, в данном случае - на языке Ада. Если разрыв слишком велик, то может потребоваться разбиение пакета "портфель-владелец" на подструктуру, состоящую из нескольких пакетов.
Например, в предложенной подструктуре пакетов запросы верхнего уровня могут быть разбиты по операциям в промежуточных пакетах на наборы более простых запросов. Затем они адресуются к пакету, который осуществляет непосредственный доступ к структурам данных. Такие промежуточные преобразования позволяют устранить вышеупомянутый семантический разрыв.
Имеется ряд других причин, по которым имеет смысл производить декомпозицию на подструктуры программы, представляющей собой базу данных, даже если видимый семантический разрыв между запросами пользователя к базе данных и операциями, определенными внутри базы данных, отсутствует. Для этого можно воспользоваться следующими двумя принципами.
Структуры данных должны быть представлены опосредствованно, через операции (процедуры или функции), а не через прямой доступ к структуре данных. Прямой доступ к большим структурам данных считается плохим приемом в программировании, поскольку он предполагает прямые ссылки через всю программу. В больших программах такие прямые ссылки к структурам данных могут повлечь за собой невозможность их изменения и модификации, поскольку в программе образуется слишком много мест, которые необходимо изменить, с тем чтобы отразить эти модификации. С другой стороны, косвенные ссылки с использованием пакетов обеспечивают изолированность ссылок, зависящих от конкретной реализации. Если в дальнейшем возникнет необходимость некоторым образом изменить представление данных в структуре, то это легко может быть сделано путем изменения операций внутри изолированного пакета. Никакая другая часть программы при этом не изменяется. Этот принцип применим и к нашей программе управления портфелем акций, поскольку по мере модификации программы нам будет необходимо разрешить доступ к одной и той же базе данных нескольким программным единицам. (Помимо задачи Secy_Treas.)
Частью пакета могут быть только те операции, которые необходимы для работы со структурой данных и которые обеспечивают косвенный доступ к главной структуре данных. В нашем примере большинство операций в пакете Portfolio_0wner предполагают доступ к портфелю акций. Например, пользователь может потребовать информацию о текущей стоимости некоторого пакета акций, которая может содержаться в портфеле акций организации.
В подструктуру могут быть включены и другие пакеты, которые могут либо заменить уже существующие на библиотечном уровне и не требующие дублирования программы, либо быть использованными для "сегментирования" пакета верхнего уровня Portfolio_Owner на более мелкие и более легко управляемые компоненты. Очень часто в процессе сегментирования большого модуля выясняется, что два или более сегментов имеют одну общую часть. Это позволяет выделить ее в будущем в отдельный сегмент подструктуры.
Разумеется, процесс сегментирования может зайти слишком далеко. В идеальном случае нам хотелось бы иметь подструктуру, каждый элемент которой соответствует некоторому конкретному элементу модели реального мира. Однако на практике мы имеем лишь приближение к идеалу. (Например, программисту может потребоваться включение пакетов со служебными программами для работы с устройствами ввода-вывода. Такие пакеты обычно слабо связаны с компонентами моделируемой структуры.)
Структура, приведенная на рис. 2.2, показывает результат декомпозиции пакета Portfolio_0wner на рис. 2.1 вышеописанным образом. Мы рассмотрим каждую из образующих ее компонент.
Как уже говорилось, задача Secy_Treas является стартовой задачей для этой программы. Предполагается, что ее внутренняя структура, которая здесь не приводится, напоминает интерпретатор отдельных команд и представляет собой некую "оболочку", отвечающую на повторяющиеся команды пользователя - секретаря-казначея организации. Каждая команда переводится в обращение к пакету Club_Portfolio для последующего выполнения одной из общедоступных операций.
Club_Portfolio служит интерфейсом между своим непосредственным пользователем (Secy_Treas) и самим портфелем акций. Он получает и интерпретирует запросы от пользователя, разбивая их на наборы более простых запросов (операций), которые выполняются пакетом Portfolio_Mgr. Club_Portfolio является пакетом-владельцем. Он владеет элементом портфеля, созданным с помощью пакета Portfolio_Mgr, и задает набор ориентированных на пользователя интерактивных операций запроса и обновления содержимого портфеля акций. (Club_Portfolio также включает в себя ряд других операций, не требующих доступа к портфелю акций, но обращающихся к другим библиотечным пакетам.)
Portfolio_Mgr определяет структуру данных типа "портфель", а также набор операций над его элементами. Portfolio_Mgr представляет собой пакет-преобразователь. К его операциям относятся операция создания (создание портфеля) и различные операции запроса и обновления, более элементарные, чем операции, входящие в пакет Club_Portfolio. Эти операции образуют минимальный набор, необходимый для осуществления доступа к структуре данных портфеля. Portfolio_Mgr используется для изолирования тех операций доступа к портфелю внутри одного пакета, которые требуют знания структуры портфеля. Вне пакета Portfolio_Mgr структура портфеля остается неизвестной, поскольку нет необходимости в том, чтобы она была известной.
Stock_Mkt_lnfo представляет собой библиотечный пакет, обновляемый ежедневно национальной телеграфной службой в дни работы фондовой биржи. Для большего приближения нашей задачи к реальности предположим, что этот пакет доступен любой организации, относящейся к NAIC. Предположим сначала, что этот библиотечный пакет поставляется ежедневно в виде магнитной ленты или диска и устанавливается затем на устройство ввода-вывода системы до момента начала работы нашей программы. В гл. 7 мы рассмотрим, как можно изменить систему, с тем чтобы смоделировать ситуацию, при которой данные будут передаваться по телеграфному каналу в реальном масштабе времени.
Text_IO представляет собой библиотечный пакет из языка Ада, содержащий различные доступные пользователю операции ввода-вывода. Text_IO доступен для пакета Club_Portfolio, а также для Secy_Treas.
Остальные три пакета, показанные на рисунке, должны рассматриваться в терминах пакета Portfolio_Mgr. Пакет Portfolio_Mgr, подробно описываемый ниже, поддерживает информацию об индивидуальных акциях, принадлежащих организации в данный момент. Эта информация хранится в очередях, причем каждому типу акций соответствует отдельная очередь. Когда организация решает продать часть своих акций, она может захотеть продать те, которые хранятся наиболее долго для того, чтобы получить при этом максимально возможную прибыль. Мы полагаем, что записи о новых закупках всегда помещаются в конец очереди, поэтому сведения о закупке этих акций находятся в начале очереди.
Purchase_Queue_Mgr. Индивидуальные очереди структур данных создаются по запросу PortfoHo_Mgr при попытке записи информации о покупке акций, не содержащихся в данный момент в портфеле. Portfolio_Mgr назначает дополнительный пакет Purchase_Queue_Mgr ответственным за операции по созданию, постановке и удалению из очередей элементов этих очередей.
Queue_Mgr. Предположим далее, что Purchase_Queue_Mgr сам является элементом некоторой парадигмы (в терминах языка Ада - родовой пакет (generic)) с именем Queue_Mgr. В дальнейшем мы дадим более подробное описание Queue_Mgr, а также правил работы с ним.
Stock_Types_And_Constants. В языке Ада пакет может состоять только из одних определений типа, определений констант, объявлений переменных или любой допустимой их комбинации. Мы помещаем в Stock_Types_And_Constants все определения типа данных, констант и переменных (делая их, следовательно, общедоступными), которые являются общими для зависящих от этих определений пакетов.
На рис. 2.2 стрелки указывают главные взаимосвязи "ссылок". Граф
означает, что А "ссылается" на В. В зависимости от того, на что делается ссылка, такая взаимосвязь имеет различный смысл. Если ссылка в В есть процедура, то А ссылается к В с целью вызвать процедуру из В. Если ссылка в В есть константа или тип, то А ссылается к В с целью извлечения константы или типа. Если ссылка в В есть переменная, то А ссылается к В с целью получения текущего значения этой переменной.
Введение "ссылочных" связей оказывает влияние на порядок компиляции. В языке Ада требуется, чтобы спецификация программной единицы В была скомпилирована до компиляции любой другой программной единицы, которая "ссылается" к В. Таким образом, для приводимой выше диаграммы спецификация В должна быть скомпилирована раньше, чем любая часть А (спецификация или тело) произведет ссылку к В. (Программы на языке Ада компилируются из индивидуальных программных единиц с использованием набора зависимых ссылок. Они обычно собираются в "библиотеке программ" по мере обработки компилятором набора программных единиц. Мы не будем касаться "последовательности компиляции в языке Ада". Подробности можно узнать в гл. 10 справочного руководства по языку Ада [21].)
Для дальнейшего изучения нашего примера мы предполагаем, что структура данных типа "портфель акций" содержит информацию, упорядоченную по следующим трем уровням:
Приведенное описание портфеля позволяет выбрать специфические представления для портфеля в целом и для всех его компонент. В языке Ада этот выбор может быть выражен через описания типа (type). Теперь мы почти готовы приступить к рассмотрению и обсуждению описания этой программы на языке Ада. Однако мы в очередной раз попросим читателя запастись терпением, с тем чтобы рассмотреть еще две очень интересных версии.
Эти версии будут рассмотрены в двух последующих подразделах, после чего мы перейдем к обсуждению операторов языка Ада для всех трех версий данной программы.
"Рядовые" члены организации по управлению капиталовложениями могут также получить доступ к портфелю акций, выдавая запросы только на чтение информации из базы данных. Модель, приведенная на рис. 2.1, может быть расширена таким образом, что для каждого члена клуба будет заведена отдельная задача, как показано на рис. 2.3. Мы хотим, чтобы Portfolio_Owner принимал и отвечал соответствующим образом на запросы, выдаваемые всеми задачами, но отвергал запросы на создание и обновление портфеля, выданные задачами, помеченными как "Member" (рядовой член клуба). Каким образом может быть модифицирована программная структура, приведенная на рис. 2,2, для обеспечения такого множественного доступа?
Рассмотрим сначала простое решение, которое предполагает, что доступ к портфелю секретаря-казначея и членов организации никогда не будет происходить параллельно. Реализация этого требования продемонстрирована на рис. 2.4. Пакет Club_Porifolio и его вспомогательные подструктуры используются двумя программными структурами. Структура программы секретаря-казначея не изменилась, но добавилась дополнительная программная структура, представляющая члена организации.
[Мы полагали, что ни одна задача, соответствующая рядовому члену организации, не может выполняться параллельно с задачей Secy_Treas, однако мы не указали то, каким способом это будет реализовано. Будем считать, что подобного рода взаимное исключение поддерживается вне самой программы. Это не подразумевает участия самих членов организации. Данное условие может быть реализовано на уровне файловой системы объектов, описанной в гл. 10.]
Стартовая задача Member обращается к Club_Portfolio только косвенно, через новый пакет Member_Ops (который доступен каждой задаче Member). Пакет Member_Ops поддерживает внутри Club_Portfolio только операции запроса. Member_Ops содержит фактически только те операции, которые имеют те же самые имена, что и доступные операции запросов в Club_Portfolio. Спецификации операций в Member_Ops идентичны с одноименными операциями в Club_Portfolio. Как мы увидим в дальнейшем при анализе программных кодов, части "тел" операций в части тела Member_Ops представляют собой всего-навсего "тождественные преобразования". Это означает, что они просто вызывают соответствующие операции в Club_Portfolio. Ограничение в пакете Member_Ops на выполнение только запросов Club_Portfolio обеспечивает невозможность обновления портфеля акций стартовой задачей Member.
Каждая программная единица, написанная на языке Ада и предназначенная для раздельной компиляции, может быть снабжена списком with. Этот список содержит те (видимые) части пакетов, к которым программная единица может осуществлять непосредственный доступ. Так, список with, предшествующий стартовой задаче Member, будет включать в себя Member_Ops, а не Club_Portfolio, а список with, предшествующий Member_Ops, содержит Club_Portfolio. Такой метод включения в списки with имен соответствующих пакетов снабжает компилятор информацией для эффективного контроля соответствующих операций с портфелем.
Наша цель в данном случае - сделать видимые операции Member_Ops соответствующим подмножеством видимых операций для Club_Portfolio. Это подмножество архитектуры i432 называют "уточнением" большего набора операций. Использование имеющейся в i432 возможности подобного уточнения позволяет определить Member_Ops как физическое подмножество Club_Portfolio. Для контроля доступа к этим подмножествам могут быть использованы специальные, распознаваемые аппаратурой дескрипторы. При этом информация внутри подмножества недоступна любой программе, имеющей доступ к этому подмножеству. Уточнения позволяют пользователю системы i432 осуществлять контроль за распределением информации без обычных недостатков, связанных с предоставлением слишком больших привилегий доступа. Мы рассмотрим эти уточнения в гл. 4.
В этом разделе мы попытаемся ослабить ограничение, запрещающее обновление портфеля акций секретарем-казначеем в тот момент, когда другие члены организации выдают запросы к портфелю. Для этого нам необходимо перейти от случая двух или более независимых и не обменивающихся друг с другом информацией задач (как в разд. 2.3.3) к набору взаимозависимых задач. Это может быть осуществлено при использовании имеющихся в языке Ада средств межзадачной коммуникации.
Рассматриваемая задача требует большего числа управляющих компонент. Нам необходимо дерево задач (а не лес задач). Структура такого рода приведена на рис. 2.5. В ней имеется только одна стартовая задача, которую мы назовем Task_Master. Эта задача представляет собой верхний уровень (корневой узел) дерева задач. В реальном мире задаче Task_Master соответствует агент, назначаемый организацией для контроля доступа сотрудников в помещение, где расположены терминалы ЭВМ, или же, что более правдоподобно, операционная система, контролирующая процедуру "установления сеанса связи". Task_Master в нашей модели несет ответственность за порождение задач для членов организации, принадлежащих к заданному "классу": президента, секретаря-казначея или рядового члена организации. (Интересный вариант обсуждается в гл. 3.)
Предположим теперь, что имеется вспомогательная база данных, в которой поддерживается и обновляется список сотрудников организации (имя, должность, и т.д.). Использование этого списка позволяет нам поставить доступ члена организации в зависимость от его должности. Пакет Task_Master может быть организован таким образом, что он порождает одну задачу для каждого члена организации при инициализации системы или же ждет, порождая задачу Member в тот момент, когда данный сотрудник начинает сеанс связи с системой.
Поскольку теперь имеются две базы данных, то несколько членов клуба, возможно, захотят осуществлять одновременный доступ, не думая при этом о возможных конфликтных ситуациях при осуществлении доступа и не беспокоясь о необходимости предотвращения несанкционированного доступа. Требуемый контроль над предотвращением конфликтных ситуаций может быть предоставлен "третьим лицам". Например, организация может назначить для каждой базы данных своего "хранителя", предоставив ему ответственность за проверку правильного и санкционированного использования базы данных. Эта дополнительная структура легко может быть отражена в нашей программной модели.
В решении, показанном на рис. 2.5, Task_Master порождает две задачи-"обслуживатели": Portfolio_Server и Roster_Server. Задаче Roster_Server предоставляется непосредственный доступ к пакету Membership_Roster. Индивидуальные задачи Member могут параллельно выдавать запросы к Portfolio_Server, но одновременно будет обслужен только один запрос, чем разрешается проблема возникновения конфликтных ситуаций при работе с портфелем акций.
Задача Portfolio_Server обращается к Roster_Server для подтверждения привилегий задачи Member, которая в свою очередь выдала некоторый запрос к портфелю. (Задача Roster_Server одновременно обслуживает только один запрос.) Привилегии доступа задачи Member являются функцией "должности" сотрудника и проверяются задачей Roster_Server. Приводимая стратегия проверки может быть расширена. Например, секретарь клуба может быть единственным сотрудником, которому открыт доступ к операциям внутри Roster-Server, включающим в себя обновление списка. (Мы не будем рассматривать это улучшение.)
Взяв за основу предложенную подструктуру системы, мы можем выработать допущения, требуемые для различных типов служб, связанных с доступом к портфелю, помимо тех, которые мы рассмотрели в системе с "несколькими отдельными пользователями" в разд. 2.3.3. Мы можем, например, рассмотреть использование следующих видов контроля:
Тип запроса | Лицо, обладающее полномочиями на получение данных услуг |
---|---|
только запрос | любой член организации |
обновление портфеля | только секретарь-казначей |
создание портфеля | президент вместе с секретарем-казначеем, работающие совместно |
удаление портфеля | президент, вице-президент и секретарь-казначей, все трое, работающие вместе |
В конце данной, а также следующей главы мы рассмотрим отдельные наиболее важные части программы, написанной на языке Ада для всех трех версий нашей системы. Полный текст программ приведен в приложениях В-Д.