Абстрактный тип данных
5.2. Операции абстрактного типа данных
При проектировании абстрактного типа данных всегда необходимо решить две задачи:
- определить представление и множество значений;
- определить множество операций типа.
Абстрактный тип может описывать достаточно сложный объект реального мира, и все его значения представляют собой домен множеств возможных значений, который проще задавать в терминах отдельных элементов. Например, для абстрактного типа "Студент" необходимо задать множество возможных значений его имени, его фамилии, номера группы, номера зачетки. Здесь имя, фамилия, номер группы и номер зачетки представляют собой элементы объекта "Студент". Задав длины элементов и наборы символов, из которых эти элементы могут составляться, можно говорить о том, что задано множество значений абстрактного типа "Студент".
Следующий шаг - это определение операций над типом. Можно определить достаточно много различных операций, однако их большое число будет загромождать и усложнять абстракцию. С другой стороны, необходимо предусмотреть достаточный набор операций для поддержания выбранного уровня абстракции.
Операции, которые выполняются над абстрактным типом данных, могут быть логически разделены на несколько групп:
- операции-конструкторы (порождают все множество возможных значений типа);
- операции инициализации (начальные конструкторы, которым безразлично исходное значение объекта);
- операции создания, удаления (позволяют занять или освободить память объекта);
- операции копирования значения типа;
- операции-селекторы (обеспечивают доступ к частям объекта);
- операции сравнения значений;
- операции преобразования типов;
- операции ввода/вывода.
Любой абстрактный тип должен иметь операции, позволяющие строить значения этого типа. Эти операции называются операциями- конструкторами. Их должно быть достаточно для порождения всего множества значений типа. Простейшим примером является операция x++. Как правило, подобные операции используют исходное состояние объекта для порождения его нового состояния.
Среди конструкторов часто необходимо выделить начальные конструкторы - операции инициализации. Им безразлично, в каком состоянии находился объект. Они сами полностью определяют его значение.
Если абстрактный тип имеет много элементов, задание нужных значений может быть выполнено в виде операций над отдельными частями объекта абстрактного типа.
Порождение всего множества допустимых значений может осуществляться не только средствами операций-конструкторов, но и при помощи операций ввода, преобразования типа, доступа к отдельным элементам типа (операции-селекторы) и т.п.
В нашем случае можно определить операцию создания
Create Fraction(FRACTION Object);
в функции, которой вменялось бы инициализировать и числитель, и знаменатель дроби. Причем числитель (Numerator) обнуляется, а знаменатель (Denominator) устанавливается в единицу. Таким образом, операция создания может одновременно рассматриваться как начальный конструктор.
Часто бывают нужны операции доступа к значению числителя или знаменателя дроби по отдельности. Это типичные операции- селекторы, которые, в свою очередь, можно еще и рассматривать как преобразователи типа, так как они из дроби получают целое число.
Поскольку обычно язык программирования не содержит функций ввода/вывода пользовательских типов, то их часто включают в операции над абстрактным типом. Иногда можно обойтись и без них, например, если печатать дробь частями, т.е. сначала ее числитель, а потом знаменатель, воспользовавшись для этого средствами, определенными для целого типа.
Операции преобразования типов абсолютно необходимы в языках программирования со строгой типизацией. Однако и в любом абстрактном типе эти операции полезны, так как дают прогнозируемый результат. При проектировании операций преобразования не нужно стремиться охватить все типы. Например, для дроби может потребоваться только операция проверки знака, приводящая дробь к логическому значению (0, 1) или типу BOOL (TRUE, FALSE).
Операции отношения позволяют сравнивать значения переменных по заданным абстракцией правилам. Так, для типа "Студент" допустимо сопоставление записей по номеру курса, значение которого является производным от номера группы. Для поиска студента в списке может оказаться полезным отношение порядка, определенное на поле фамилии, а для деканата будет полезным сравнение учащихся по успеваемости. Для типа FRACTION кроме сравнений на равно/неравно и больше/меньше может потребоваться сравнение знаменателей.
Дополнительно к операциям сравнения необходимо учитывать возможность проверки предусловий любой операции. Например, перед выполнением операции деления должна быть возможность удостовериться, что делитель не нуль. Это можно сделать преобразованием делителя к целому числу или при помощи специальной функции сравнения с нулем. Эта специальная функция тоже будет относиться к классу операций сравнения.
Обычно абстрактный тип является сложной структурой и включает в себя несколько элементов, при этом бывает необходимо получить или изменить значение только одного из них, например получить номер зачетной книжки записи "Студент". Это выполняется при помощи операций-селекторов.
При реализации операций-селекторов надо быть осторожным и не допускать, чтобы с помощью этих операций пользователь мог нарушить целостность типа. Например, нельзя присвоить записи "Студент" номер зачетки, не соответствующий коду его факультета, или для дроби задать нулевой знаменатель.
Операции копирования необходимы в случаях сложных типов, так как простое присваивание обычно не будет достаточным и способ присваивания (копирования значения) зависит от реализации типа. Например, копирование списка требует создания памяти для представления копий элементов и перезаписи значений из памяти одного списка в другой.
При проектировании набора операций очень важно сохранять взаимосвязь проектируемых операций со свойствами самого абстрактного объекта. Например, в случае "Студент" вполне осуществимой операцией может быть прибавление числа к номеру зачетки, однако такая операция не согласуется с абстракцией типа "Студент". А прибавление единицы к числителю дроби (FRACTION) может быть вполне оправдано.
Наконец, не всегда над абстрактным типом можно выполнить все заданные операции. Бывают случаи, когда текущее его значение такого, что некоторые операции становятся недопустимыми, например получение обратной дроби от нуля. Поэтому экспортирующий абстрактный тип программный модуль должен предоставлять средства проверки допустимости выполнения операции - проверки предусловия операции.
Таким образом, при определении операций над абстрактным типом данных (АТД) рекомендуется придерживаться следующих правил.
- Должна быть обеспечена возможность проверки предусловий каждой экспортируемой операции.
- Следует оценить целесообразность включения в АТД следующих операций:
-
- операции создания;
- операции инициализации;
- операции перемещения (копирования структуры, значений);
- операции сравнения;
- операции преобразования типов;
- операции ввода/вывода;
- операции копирования (значений);
- операции-селекторы;
- операции уничтожения.
- Старайтесь свести число операций к минимуму. Простой АТД проще понять.
- Поддерживайте связь операций с выбранной абстракцией типа.