2.5. Привилегия "только запрос" к портфелю

В этом разделе мы рассмотрим реализацию, предоставляющую задаче доступ "только запрос" к портфелю, как это проиллюстрировано на рис. 2.4. В ней используется решение, предложенное в разд. 2.3.3. Как говорилось в этом разделе, задача, обладающая привилегией "только запрос", осуществляет доступ к Club_Portfolio через "промежуточный" пакет-преобразователь с именем Member_Ops. Сейчас мы рассмотрим подробности реализации этого промежуточного пакета.

На рис. 2.18 и 2.19 приведен пример программирования тела и спецификации для Mernber_Ops.

--------------------- Пакет Member_Ops, версия 1 --------------------------
with Club_Portfolio, Stock_Types_And_Constants;
package Member_Ops is
   use Club_Portfolio, Stock_Types_And_Constants;
   function Print_club_valuation
      return dollars;
   procedure Print_club_holdings;
   function Find_stock_code(corporate_name: in long_string)
      return stock_code_pair;
   procedure Print_individual_stock_summary(stock_code: in stock_code_pair);
   -- и т. д. . . . . .
end Member_Ops

Рис. 2.18. Спецификация части спецификации Mernber_Ops.


---------------------- Тело Member_Ops, версия 1 ----------------------------
package body Member_Ops is
   -- Приводимые здесь объявления тел каждой из подпрограмм представляют
   -- собой обращение к соответствующей процедуре в Club_Portfolio.
   function Print_Club_valuations return dollars
      is begin return Club_Portfolio.Print_club_valuation; end;
   procedure Print_club_holdings
      is begin Club_Portfolio.Print_club_holdings; end;
   function Find_Stock_Code(corporate_name: in long_string) return stock_code_pair
      is begin return Club_Portfolio.Find_stock_code(corporate_name); end;
   procedure Print_individual_stock_summary(stock_code: in stock_code_pair)
      is begin Club_Portfolio.Print_individual_stock_summary(stock_code); end;
   -- и т. д. . . . 
   -- никаких локальных объявлений и инициализации не требуется
end Member_Ops;

Рис. 2.19. Тело пакета Member_Ops, версия 1.
   

Как видно из рис. 2.19, тело каждой процедуры, приводимой в части тела Member_Ops, имеет простую структуру. Оно состоит из повторяющейся части спецификации, за которой следует обращение к процедуре с идентичным именем из пакета Club_Portfolio, например:

   <Спецификация Р>
   is
   begin
      Club_Portfolio.P;
   end;
   

Как показано на рис. 2.18 и 2.19, каждый вызов общедоступной операции пакета Member_Ops приводит к дополнительному и, очевидно, избыточному межпакетному вызову соответствующей операции из Club_Portfolio. Используя имеющееся в языке Ада указание (pragma), говорящее о том, что задача является открытой, мы можем создать хороший компилятор, позволяющий избегать компиляции этого промежуточного уровня. Если каждая процедура в пакете Member_Ops объявлена "открытой" (INLINE), то издержки, связанные с дополнительным вызовом процедуры, устраняются. В этом случае вместо вызова процедуры из Member_Ops тело этой процедуры подставляется в точку вызова, напоминая этим процесс подстановки макрорасширений в некоторых ассемблерах. Таким образом, внешнее обращение к процедуре пропадает. Эффект заключается в том, что обращение происходит непосредственно к процедуре в Club_Portfolio, минуя процедуру в Member_Ops.

Мы можем сделать два наблюдения, относящиеся к рис. 2.18:

  1. Отметим, что список with для Member_Ops включает в себя только два пакета - Club_Portfolio и Stock_Types_And_Constants. Исключение пакета Portfolio_Mgr приводит к тому, что из Member_Ops невозможно обратиться ни к одной из его операций. Следовательно, пакет Club_Portfolio является единственным каналом, через который Member_Ops может обращаться к Portfolio_Mgr. Аккуратное использование механизма with позволяет реализовать любую необходимую нам форму контроля.

  2. Отметим то, каким образом мы разрешили потенциальное противоречие в телах подпрограмм внутри пакета Member_Ops. Каждая процедура Р внутри пакета Member_Ops содержит обращение к подпрограмме из Club_Portfolio, имеющей точно такое же имя и такой же список параметров, как и Р. Эта неоднозначность снимается путем использования имени "Club_Portfolio" в качестве префикса. Префикс необходим для разрешения видимой неоднозначности несмотря на то, что мы включили идентификатор Club_Portfolio в список use для Member_Ops. При отсутствии префикса компилятор проинтерпретирует каждое предполагаемое обращение к Р в Club_Portfolio как рекурсивное обращение P в пакете Member_0ps. [Более подробное рассмотрение этого момента читатель может найти в восьмой главе справочного руководства по языку Ада [2], в котором рассматриваются правила видимости, принятые в этом языке.]

    Оператор use

       use Club_Portfolio, Stock_Types_And_Constants;
             

    информирует компилятор о том, что типы, которые не были объявлены в части спецификации пакета Member_Ops, как, например, percent, stock_code_pair, dollar и т. д., могут быть найдены в одном из пакетов, перечисленных в списке use. Если список use отсутствует, то имена этих типов должны быть приведены полностью, т.е. Club_Portfolio.percent, Stock_Types_And_Constants.stock_code_pair, Stock_Types_And_Constants.dollar, и т. д.

    [Читатели, знакомые с языком Паскаль, увидят, что список use напоминает оператор Паскаля with, который также является удобным механизмом выделения общего контекста для отдельных компонент. Используемый в языке Ада список use допускает, однако, гораздо более широкий диапазон конструкций.]

---------------------- Member_Ops, версия 2 -----------------------------
with Club_Portfolio, Stock_Types_And_Constants;
package Member_Ops is
   use Club_Portfolio, Stock_Types_And_Constants;
      -- Операции этого пакета идентичны операциям с аналогичными именами в
      -- пакете Club_Portfolio.
      -- Объяснения этих функций приводятся в том пакете.
   function Print_Club_valuation
      return dollars
      renames Club_Portfolio.Print_Club_valuation;
   procedure Print_club_holdings
      renames Club_Portfolio.Print_club-valuation;
   function Find_Stock_Code(corporate-name: in long_string)
      return stock-code_pair
      renames Club_Portfolio.Find_stock_code;
   procedure Print_individual_stock_summary(stock_code: in stock_code_pair)
      renames Club_Portfolio.Print_individual_stock_summary;
   -- и т.д. . . . 
end Member_Ops;   -- В данном случае тело пакета отсутствует.

Рис. 2.20. Вторая версия Member_Ops с использованием возможности renames.
   

Рассмотрим теперь второй вариант программирования пакета Member_Ops. В этом примере обращения к операциям запроса пакета Member_Ops являются непосредственными обращениями к соответствующим операциям в Club_Portfolio. В этом подходе используется имеющаяся в языке Ада возможность renames, что иллюстрируется на рис. 2.20. Часть спецификации для каждой операции в Member_Ops заменяется объявлением renames. Соответствующая часть тела при этом не требуется.

Подводя итоги, мы можем видеть, что для расширения структуры, приведенной на рис. 2.2, до структуры, приведенной на рис. 2.4, необходимо только добавить промежуточный или "фильтрующий" пакет - Member_Ops, общедоступные операции которого представляют собой подмножество из Club_Portfolio. Применение Member_Ops в структуре пакета дает нам возможность предоставления пользователям привилегий "только запроса" к экземпляру портфеля. Использование свойства "inline" или renames обеспечивает отсутствие лишних накладных расходов, связанных с дополнительным уровнем адресации.

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

назад оглавление вперед
Рейтинг ресурсов "Весь Екатеринбург"