Раздел 11.4 - Сравнение подходов GADT и GADO

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

Все же, он еще не столь гибок, как мог бы быть. Нельзя передавать стеки в качестве параметров, нельзя также создавать массивы стеков (а вдруг нам захочется их иметь!).

Мы могли бы получить большую гибкость, описывая внутри пакета новый тип (например, "Stack") и изменяя каждую операцию при его использовании. Затем, используя этот тип, можно передавать стеки в качестве параметров, создавать массивы и т.п. Вот пример того, как могла бы выглядеть такая спецификация пакета Generic_Stack:

  generic
    Size : Positive;
    type Item is private;
  package Generic_Stack is
    type Stack is limited private;
    procedure Push(S : in out Stack; E : in  Item);
    procedure Pop (S : in out Stack; E : out Item);
    Overflow, Underflow : exception;
  private 
    type Stack is array(1 .. Size) of Item; 
  end Generic_Stack;

Сравните это с предыдущей версией нашего пакета Generic_Stack:

  generic
    Size : Positive;
    type Item is private;
  package Generic_Stack is
    procedure Push(E : in  Item);
    procedure Pop (E : out Item);
    Overflow, Underflow : exception;
  end Generic_Stack;

Новая версия пакета является примером того, что называют настраиваемым абстрактным типом данных (generic abstract data type - GADT). GADT является настраиваемым модулем, в котором описан главный тип данных и операции над ним так, что клиенты пакета могут использовать этот тип при работе с массивами и записями. Вариант, рассмотренный в предыдущем уроке называется настраиваемым абстрактным информационным объектом (generic abstract data object - GADO). Каждый экземпляр такого объекта можно представить как некий "механизм", выполняющий фиксированный набор операций (таких, как push и pop). Использование GADT несколько более трудоемко, так как необходимо конкретизировать настраиваемый тип (с помощью создания пользовательского типа), а затем описывать переменную этого типа. Для каждой операции GADT также необходимо укзывать, какой объект (например, стек) использовать. Несмотря на небольшие дополнительные усилия GADT более гибок. К примеру, если необходим массив стеков, то это просто реализовать, используя GADT. Напротив, не слишком практично создавать массив настраиваемых абстрактных объектов данных. Поэтому в общем случае рекомендуется разрабатывать настраиваемые пакеты как GADT, а не как GADO (глава 8 AQ&S это настоятельно рекомендует).

Для использования новой GADT-версии пакета Generic_Stack все еще необходима конкретизация. Вот пример (заметим, что конкретизируется он точно также):

  package Stack_Int is new Generic_Stack(Size => 200, Item => Integer);

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

  with Stack_Int; use Stack_Int; 
  ... 
  My_Stack : Stack;  -- тип Stack из пакета Stack_Int's . 

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

  Push(My_Stack, 7);

Если вы действительно заинтеросованы в том, чтобы сделать компонент многократно используемым, необходимо попытаться сделать его как можно более гибким. Например, для создания сложных структур данных нужно иметь возможность компоновать многократно используемые компоненты из других многократно используемых компонентов . Простой способ проверки гибкости - скомпоновать многократно используемый компонент из него самого [Wheeler 1992]. Хотя пакет стеков, который был продемонстрирован, весьма хорош во многих отношениях, он не слишком хорошо компонуется: невозможно создать стек стеков. Почему? Потому что тип "Stack" в новой версии пакета является ограниченным приватным типом: в нем не определена операция присваивания. В то же время, тип Item - приватный, т.к. нам необходима операция присваивания для реализации Push и Pop.

Выход прост - между стеками тоже необходимо наличие операции присваивания. Один из подходов, который сделал бы Stack приватным типом - разрешить Ada использовать операции присваивания по умолчанию применительно к массивам. Еще лучшим подходом было бы использование типа "Controlled", который мы обсудили в уроке 7, таким образом, чтобы лучше контролировать присваивание. Это можно использовать это для того, чтобы реализовать собственный оператор присваивания. Подробнее мы рассмотрим такой подход несколько позже.


Упражнение:

Что представляет собой GADT?

  1. Настраиваемый пакет, в котором нет определения новых типов.
  2. Настраиваемый пакет, в котором определен новый тип, который используется для всех операций (подпрограмм) определенных в настраиваемом пакете.
  3. Любой настраиваемый пакет.

Вы можете также:

PREVIOUS Перейти к предыдущему разделу

NEXT     Перейти к следующему разделу

OUTLINE  Вернуться к содержанию Урока 11

David A. Wheeler (dwheeler@ida.org)

Перевод: Юрий Королев   Общая редакция перевода: Г.Ю. Сисюк

Исходная копия этого документа находится по адресу "http://www.adahome.com/Tutorials/Lovelace/s11sf.htm".

Исходная копия перевода размещена на сайте http://www.ada-ru.org