В предыдущих обсуждениях мы изложили начальные сведения, необходимые для понимания структуры и семантики задач, создаваемых на языке Ада. В данном разделе мы рассмотрим фрагменты конкретных примеров, полностью приведенных в приложении Е. В этом приложении дается написанная на языке Ада задача Portfolio_Server и части спецификации задачи Roster_Server и пакета Membership_Roster. Эти программные единицы созданы в соответствии со структурой, приведенной на рис. 2.5. [В приложении В приводятся соответствующие программные единицы для структуры на рис. 3.2.]
В программной структуре, приведенной на рис. 2.5, задача Portfolio_Server перегружена рядом избыточных "ответственностей". Она должна осуществлять ряд обращений к Roster_Server. Помимо этого она должна преобразовывать запросы оператора к Club_Portfolio. Такая зависимость Roster_Server от Portfolio_Server, независимого самого по себе от Membership_Roster, уже достаточна для того, чтобы отказаться от стратегии, реализуемой в структуре, приведенной на рис. 2.5. Читатели, имеющие больший опыт в параллельном программировании, наверняка с этим уже согласились. Однако в целях упражнения мы продолжим рассмотрение и анализ структуры программы, реализующей подход, приведенный на рис. 2.5.
Тип входа | Имя входа |
---|---|
Запросы к портфелю (9). (Выполняются как обращения к операциям пакета Club_Portfolio.) |
Print_Club_portfolio Print_Club_holdings Find_stock_code Print_individual_stock_summary Print_shares_and_value_of_stock Print_average_cost Print_winners Print-loosers Print_non_movers |
Запросы на обновление портфеля (2). (Выполняются как обращения к операциям пакета Club_Portfolio после обращений к Roster_Server.) |
Enter-buy Enter_sell |
Запросы на создание и удаление портфеля (6). (Выполняются как обращения к операциям в Club_Portfolio после обращений к Roster_Server.) |
President_create_folio Vice_president_create_folio Treasurer_create_folio President_delete_folio Vice-president_delete_folio Treasurer_delete_folio |
Запросы к Membership_Roster (2). (Выполняются через обращения к Roster-Server.) |
Lookup-member List_of_members |
Обновления Membership-Roster (3). (Выполняется через обращения к Roster-Server.) |
Add-new-member Update_member Delete-member |
На рис. 3.8 приводится список входов, необходимых для задачи Portfolio_Server. Каждый из этих входов требует объявления entry в части спецификации программы Portfolio_Server.
Как видно из рис. 2.5, Portfolio_Server непосредственно зависит от спецификаций для Roster_Server и для Club_Portfolio. В свою очередь Roster_Server зависит от спецификаций для пакета Membership_Roster.
Удобно продолжить развитие Portfolio_Server с расширения спецификации Membership_Roster, а затем спецификации Roster_Server, закончив, наконец, расширением спецификации Portfolio_Server. Эта последовательность и определила список на рис. 3.8. [Вспомните, что мы уже модифицировали пакет Club_Portfolio (см. приложение В). Помните, что модификации этого пакета были сделаны с целью введения двух дополнительных операций - Delete_folio и Create_folio.]
В данном случае наше рассмотрение уклоняется от принципа "снизу вверх". Представляется более удобным и "логичным" сначала создавать структуру программы в привычной манере "сверху вниз", а части спецификации для каждого узла программной структуры разрабатывать в манере "снизу вверх" и, наконец, программировать части тела "сверху вниз". В дальнейшем мы будем придерживаться именно такого подхода; будем считать, что пакет Membership_Roster должен "владеть" своим списком членов организации приватного типа. Скелетная форма части спецификации этого пакета приведена на рис. 3.9.
with Roster_Types_And_Constants, Text_I0; package Membership_Roster is use Roster_Types_And_Constants, Text_I0; type roster is private; -- см. определение ниже. -- -- Имеются пять следующих операций (см. приложение Е): -- Lookup_member, -- Возвращает копию записи текущего сотрудника, -- List_of_members, -- Печатает список информации из журнала. -- Add_new_member, -- Добавляет запись о новом сотруднике. -- Update_member, -- Обновляет запись о текущем сотруднике. -- Delete_member -- Удаляет запись о текущем сотруднике. -- private type roster is array (1 .. max_num_members) of member_record; -- -- (Предполагается, что) экземпляр списка порождается в -- теле данного пакета. end Membership_Roster; Рис. 3.9. Скелетная структура спецификации пакета Membership_Roster.
Пять операций пакета Membership_Roster поясняются в комментариях к рис. 3.9. При успешной работе каждая из операций выполняет указанную функцию. Читатель может посмотреть полную спецификацию этих операций в приложении Е.
Отметим, что приватный тип списка зависит от константы max_num_members и от типа member_record. Эти два элемента объявляются в дополнительном пакете Roster_Types_And_Constants, который появляется в операторных скобках with и use Membership_Roster. Позднее мы увидим, что Roster_Server и Portfolio_Server также зависят от Roster_Types_And_Constants. Эта зависимость объясняется ниже.
Задача Roster_Server имеет пять объявлений входа. Эти объявления перечислены на рис. 3.10.
Тип входа | Имя входа |
---|---|
Запросы о занимаемых должностях, являющимися булевскими (4) функциями. (Выполняются через обращения к Membership_Roster.) |
Is_president Is_vice_president Is_treasurer Is_secretary |
Общие запросы к списку (2). (Выполняются через обращения к Membership_Roster.) |
Lookup_member List_of_members |
Запросы на обновление списка членов организации (3). (Выполняются через обращения к Membership_Roster.) |
Add_new_member Update_member Delete_member |
Задаче Roster_Server необходимо иметь доступ к представлению типа member_record, поскольку ей требуется давать ответ на запросы о занимаемых должностях. Для этого в своем ответе на полученный вызов от Membership_Roster. Lookup_member эта задача должна быть способна анализировать индивидуальные компоненты экземпляра member_record. В процессе выполнения запросов на обновленре журнала задачи Roster_Server и Portfolio_Server направляют индивидуальные записи member_record к Membership_Roster. По этой причине тип member_record не может быть объявлен приватным.
with Membership, Roster, Roster_Types_And_Constants; task Roster_Server is use Membership_Roster, Roster_Types_And_Constants; -- -- Запросы занимаемых должностей; -- entry Is_President( mennber_name: in string_of30; check: out boolean); -- -- Функция: -- Обращается к Membership_roster. Lookup_member для получения записи -- о member_name (от имени члена организации). Устанавливает значение -- в true, если member_name соответствует имени члена организации, чье -- имя есть President, в противном случае возвращается прежнее значение -- false. entry Is_Vice_President( member_name: in string_of30; check: out boolean); -- -- Функция: -- Аналогична функции Is_President. -- Здесь размещается вход для Is_treasurer. -- Здесь размещается вход для ls_secretary. -- -- Общие запросы к списку: -- -- Здесь размещается вход для Lookup_member. -- Здесь размещается вход для List_of_members. -- -- Запросы на обновление списка членов организации: -- -- Здесь размещается вход для Add_new_member. -- Здесь размещается вход для Update_member. entry Delete_member( my_name: in string_of30; member_name: in string_of30; check: out boolean); -- -- Функция: -- Вызывает Membership_Roster.Delete_member для удаления всей информации -- о текущем сотруднике из списка членов организации. Если удаление прошло -- успешно, то перед возвратом в переменную check устанавливается true. -- В переменную check устанавливается false, если обращение к Delete_member -- закончилось неудачно. (Это может произойти в том случае, когда значение -- переменной my_name не соответствует имени секретаря или если в списке -- еще нет сотрудника с таким именем.) end Roster_Server; Рис. 3.11. Скелетная структура части спецификации Roster_Server.
На рис. 3.11 приведена скелетная форма части спецификации задачи Roster_Server, в которой приводятся подробности для трех из девяти входов. Полный набор входов рассматривается в приложении Е.
with Club_Portfolio, Roster_Server, Stock_Types_And_Constants, Roster, Types_And_Constants; -- На рис. 2.5 и 3.2 были показаны не все из этих зависимостей. task Portfolio_Server is use Club_Portfolio, Roster_Server, Stock_Types_And_Constants, Roster_Types_And Constants; -- -- Здесь производятся запросы к портфелю (9 входов). -- -- Запросы на обновление портфеля: entry Enter_buy( my_name: in string_of30; unauthorized: out boolean; purch_date: in date; stock_code: in stock_code_pair; num_shares: in natural; per_sh_price: in dollars; commission: in dollars); -- -- Функция; -- Определяет, имеет ли сотрудник с именем, указанным в переменной my_name, -- право на обновление портфеля. Если нет, то в переменную unauthorized -- устанавливается и возвращается значение true. Если да, то в unauthorized -- подcтавляется false и затем вызывается соответствующая операция в -- Club_Portfolio. -- -- Здесь располагается вход для Enter_sell. -- ------------ Здесь помещаются операции по обслуживанию запросов на создание -- и удаление портфеля (6 входов). -- ------------ Запросы к списку членов организации (5 входов). -- end Portfolio_Server; Рис. 3.12. Скелетная структура спецификации Portfolio_Server с входом Enter_buy
Использование входов Portfolio_Server иллюстрируется в рассмотрении протокола по обновлению и удалению портфеля. На рис. 3.12 дано извлечение из части спецификации Portfolio_Server для входа Enter_buy. На рис. 3.13 в части тела Portfolio_Server показан соответствующий оператор accept.
accept Enter_buy( my_name: in string_of30; unauthorized: out boolean; purch_date: in date; stock_code: in stock_code_pair; num_shares: in natural; per_sh_price: in dollars; commission: in dollars); do Roster_Server.Is_treasurer(my_name, check_boolean); if check_boolean then -- санкционированный Club_Portfolio.Enter_buy(purch_date, stock_code, num_shares, per_sh_price, commission); unauthorized:= false; else unauthorized:=true; end if; end Enter_buy; Рис. 3.13. Иллюстрация того, каким образом оператор accept для Enter_buy обусловливает обращение к Roster_Server.Is_Treasurer
Первые два параметра для Enter_buy являются управляющими. В переменной my_name должно быть указано имя обращающегося к данной программе. Это имя должно совпадать с именем казначея организации. Параметр unauthorized возвращает значение "true", если указанное имя сотрудника организации, проверенное по входу Is_treasurer для Roster_Server, не соответствует имени казначея. С такой спецификацией для входа Enter_buy тело соответствующего оператора accept для Enter_buy (см. рис. 3.13) начинается с обращения ко входу Is_treasurer. Если этот вызов устанавливает локальную переменную check_boolean в "true", то после последующего обращения к операции Enter_buy в Club_Portfolio значение unathorized устанавливается в "false". В противном случае оно устанавливается в "true".
entry President_delete_folio( my_name: in string_of30; portfolio_name: in long_string; unauthorized: out boolean); -- -- Функция: -- Вызывает Roster_Server для определения того, является ли сотрудник со -- значением имени my_name текущим президентом организации. Если это не -- так, то в переменную unauthorized устанавливается значение true. Если это -- так, то в переменную unauthorized устанавливается значение false и затем -- это значение возвращается после записи указанного имени портфеля. -- Фактиеское удаление не произойдет до тех пор, пока не будет получена -- последовательноеть из трех запросов на удаление, по одному на каждого из -- трех членов администрации организации: президента, вице-президента -- и казначея. Рис. 3.14. Объявление входа для первого из трех обращений по удалению портфеля. entry Vice_president_delete_folio( my name: in string_of30; portfolio_name: in long_string; unautliorized: out boolean); -- -- Функция: -- Запрос к данному входу воспринимается в том и только в том случае, если -- последнее воспринятое обращение было сделано ко входу -- President_delete_folio и это обращение было санкционированным. -- Определяет, является ли в данный момент сотрудник с именем, значение -- которого содержится в переменной my_name, вице-президентом организации. -- Если это не так, то в переменную unauthorized устанавливается значение -- true. Если это так, то в переменную unauthorized устанавливается значение -- false и затем это значение возвращается после записи указанного имени -- портфеля. Фактическое удаление не произойдет до тех пор, пока не будет -- получена последовательность из трех запросов на удаление, по одному на -- каждого из трех членов администрации организации: президента, -- вице-президента и казначея. Рис. 3.15. Объявление входа для второго из трех вызовов для удаления портфеля. entry Treasurer_delete_folio( my name: in string_of30; portfolio-name: in long_string; unauthorized: out boolean; check: out boolean); -- если установлена в true, то портфель был удален. -- -- Функция: -- Обращение к данному входу принимается в том и только в том случае, если -- два последних воспринятых обращения были произведены к -- President_delete_folio и к Vice_president_delete_folio в указанном -- порядке и если оба этих запроса были санкционированными. -- Вызывает Roster_Server для определения того, является ли сотрудник -- с именем, значение которого содержится в переменной my_name, -- текущим казначеем клуба. -- Если это не так, то в переменную unauthorized устанавливается значение true -- и это значение возвращается. Если это так, то в переменную unauthorized -- устанавливается значение false. Три указанных имени проверяются. Если все -- они не идентичны, то в переменную check устанавливается значение false и -- производнтся возврат. -- Если они совпадают, то из Club_Portfolio вызывается операция Delete_folio. -- Если вызов успешный (портфель удаляется), то в переменную check -- устанавливается значение true. Затем происходит возврат к вызвавшему -- Treasurer_delete_folio. Рис. 3.16. Объявление входа для третьего из трех вызовов по удалению портфеля. accept President_delete_folio( my name: in string_of30; portfolio_name: in long_string; unauthorized: out boolean) do Roster_Server.Is_treasurer(my_name, check_boolean) if check_boolean then local_name_1:=portfolio_name; -- Сохранить копию portfolio_name -- для проверки при следующем accept. unauthorized:=false; else unauthorized:=true; end if; end President_delete_folio; -- -- Здесь начинается последовательность из двух операторов accept. -- accept Vice_president_delete_folio( my name: in string_of30; portfolio_name: in long_string; unauthorized: out boolean) do Roster_Server.Is_vice_president(my_name, check_boolean) if check_boolean and local_name_1=portfolio_name then lосаl_name_2:=portfolio_name; -- Сохранить копию portfolio_name для -- проверки при следующем accept. unauthorized:=false; else unauthorized:=true; end if; end Vice_president_delete_folio; -- accept Treasurer_delete_folio( my name: in string_of30; portfolio_name: in long_string; unauthorized: out boolean; check: out boolean); -- если установлена в true, то портфель был удален. do Roster_Server.Is_treasurer(my_name, check_boolean) if check_boolean and local_name_1=portfolio_name and local_name_2=portfolio_name then unauthorized:=false; --Доступ разрешен. Club.Portfolio.Delete_folio(portfolio_name, check); -- Портфель был удален, если значение, возвращенное в -- переменной check есть true. else unauthorized:=true; end if; end Treasurer_delete_folio; -- -- Конец последовательности (конец последовательности из трех операторов -- accept). Рис. 3.17. Последовательность из трех операторов accept, которая должна быть выполнена для удаления элемента экземпляра портфеля.
Последний пример в данном разделе иллюстрирует три операции, необходимые для удаления портфеля. На рис. 3.14≈3.16 приведен набор из трех объявлений входа в Portfolio_Server, к которым для завершения процедуры удаления портфеля должно быть произведено обращение. На рис. 3.17 показана соответствующая последовательность трех операторов accept для части тела Portfolio_Server. Комментарии, предлагаемые для трех объявлений входа в Portfolio_Server, должны убедить читателя в том, что приводимая последовательность операторов accept в точности отражает эти спецификации.