Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Основы разработки программного обеспечения на примере языка С. Учебное пособие для СПО
.pdf
Если абстрактный тип имеет много элементов, задание нужных
значений может быть выполнено в виде операций над отдельными
частями объекта абстрактного типа.
Порождение всего множества допустимых значений может
осуществляться не только средствами операций-конструкторов, но и
при помощи операций ввода, преобразования типа, доступа к
отдельным элементам типа (операции-селекторы) и т.п.
В нашем случае можно определить операцию создания
Create Fraction(FRACTION Object);
в функции, которой вменялось бы инициализировать и числитель, и
знаменатель дроби. Причем числитель (Numerator) обнуляется, а
знаменатель (Denominator) устанавливается в единицу. Таким образом,
операция создания может одновременно рассматриваться как
начальный конструктор.
Часто бывают нужны операции доступа к значению числителя или
знаменателя дроби по отдельности. Это типичные операцииселекторы, которые, в свою очередь, можно еще и рассматривать как
преобразователи типа, так как они из дроби получают целое число.
Поскольку обычно язык программирования не содержит функций ввода/
вывода пользовательских типов, то их часто включают в операции над
абстрактным типом. Иногда можно обойтись и без них, например, если
печатать дробь частями, т.е. сначала ее числитель, а потом знаменатель,
воспользовавшись для этого средствами, определенными для целого
типа.
Операции преобразования типов абсолютно необходимы в языках
программирования со строгой типизацией. Однако и в любом
абстрактном типе эти операции полезны, так как дают прогнозируемый
результат. При проектировании операций преобразования не нужно
стремиться охватить все типы. Например, для дроби может
потребоваться только операция проверки знака, приводящая дробь к
логическому значению (0, 1) или типу BOOL (TRUE, FALSE).
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
121

Операции отношения позволяют сравнивать значения переменных по
заданным абстракцией правилам. Так, для типа "Студент" допустимо
сопоставление записей по номеру курса, значение которого является
производным от номера группы. Для поиска студента в списке может
оказаться полезным отношение порядка, определенное на поле
фамилии, а для деканата будет полезным сравнение учащихся по
успеваемости. Для типа FRACTION кроме сравнений на равно/неравно
и больше/меньше может потребоваться сравнение знаменателей.
Дополнительно к операциям сравнения необходимо учитывать
возможность проверки предусловий любой операции. Например, перед
выполнением операции деления должна быть возможность
удостовериться, что делитель не нуль. Это можно сделать
преобразованием делителя к целому числу или при помощи
специальной функции сравнения с нулем. Эта специальная функция
тоже будет относиться к классу операций сравнения.
Обычно абстрактный тип является сложной структурой и включает в
себя несколько элементов, при этом бывает необходимо получить или
изменить значение только одного из них, например получить номер
зачетной книжки записи "Студент". Это выполняется при помощи
операций-селекторов.
При реализации операций-селекторов надо быть осторожным и не
допускать, чтобы с помощью этих операций пользователь мог нарушить
целостность типа. Например, нельзя присвоить записи "Студент" номер
зачетки, не соответствующий коду его факультета, или для дроби задать
нулевой знаменатель.
Операции копирования необходимы в случаях сложных типов, так как
простое присваивание обычно не будет достаточным и способ
присваивания (копирования значения) зависит от реализации типа.
Например, копирование списка требует создания памяти для
представления копий элементов и перезаписи значений из памяти
одного списка в другой.
При проектировании набора операций очень важно сохранять
взаимосвязь проектируемых операций со свойствами самого
абстрактного объекта. Например, в случае "Студент" вполне
осуществимой операцией может быть прибавление числа к номеру
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
122

зачетки, однако такая операция не согласуется с абстракцией типа
"Студент". А прибавление единицы к числителю дроби (FRACTION)
может быть вполне оправдано.
Наконец, не всегда над абстрактным типом можно выполнить все
заданные операции. Бывают случаи, когда текущее его значение такого,
что некоторые операции становятся недопустимыми, например
получение обратной дроби от нуля. Поэтому экспортирующий
абстрактный тип программный модуль должен предоставлять средства
проверки допустимости выполнения операции - проверки предусловия
операции.
Таким образом, при определении операций над абстрактным типом
данных (АТД) рекомендуется придерживаться следующих правил.
Должна быть обеспечена возможность проверки предусловий
каждой экспортируемой операции.
Следует оценить целесообразность включения в АТД следующих
операций:
операции создания;
операции инициализации;
операции перемещения (копирования структуры, значений);
операции сравнения;
операции преобразования типов;
операции ввода/вывода;
операции копирования (значений);
операции-селекторы;
операции уничтожения.
Старайтесь свести число операций к минимуму. Простой АТД
проще понять.
Поддерживайте связь операций с выбранной абстракцией типа.
5.3. Реализация абстрактного типа
Реализация АТД обычно производится в форме отдельного
программного модуля. В заголовочном файле отражается абстрактный
взгляд на тип данных. Имена операций конструируются в терминах
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
123

выбранной абстракции и описываются в терминах, понятных
импортеру на уровне, необходимом для импортера. Например, в АТД
FRACTION для функции ConvertToFixPoint достаточно указать
лишь, что на входе у нее простая дробь, а на выходе - соответствующее
ей число с фиксированной точкой. Как выполняется операция,
указывать не требуется, так как клиенту это не важно. С другой стороны,
для операций должны подробно указываться все пред- и постусловия,
необходимые для ее выполнения, в понятных импортеру терминах (как
разделяется в результате целая и дробная части, где расположен знак и
т.п.).
Утверждения (предусловия, постусловия и инварианты модуля) в
модуле определения называют абстрактными утверждениями. Они суть требования или спецификация модуля как для разработчика, так и
для клиента. Все условия, расположенные в модуле реализации,
называют утверждениями реализации. Если модуль корректен, то все
абстрактные утверждения поддерживаются утверждениями реализации.
Если пользователь обеспечил все абстрактные предусловия и
инварианты перед запуском операции, то он вправе ожидать, что после
ее выполнения все абстрактные постусловия и инварианты будут
выполнены. Разработчик модуля имеет несколько иную точку зрения он вправе рассчитывать, что все предусловия и инварианты
реализации заведомо истинны перед вызовом операции, однако его
задача состоит в том, чтобы обеспечить выполнения всех постусловий
и инвариантов после завершения операции.
Далее приведем пример реализации абстрактного типа данных обыкновенная дробь. Она будет состоять из числителя и знаменателя,
при этом всегда будет сокращенной. Пусть знаменатель всегда будет
положительным, а числитель будет определять знак (положительный
или отрицательный) для всей дроби. Остается лишь определить, что
нулевая дробь - это дробь 0/1 (т.е. знаменатель равен 1).
В качестве операций следует рассмотреть стандартные арифметические
операции - сложение, вычитание, умножение, деление, обязательные
для абстрактного типа данных операции сравнения и копирования,
операцию создания/инициализации. Дополнительно логично
реализовать операцию преобразования к десятичной дроби, операцию
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
124

построения дроби из целого числа и операции-селекторы - получение
числителя и получения знаменателя.
Код будет организован в виде двух файлов - заголовка "fraction.h" и
файла реализации "fraction.c":
----- файл fraction.h ----/*******************************************************
Date: 10 January 2013
Description: fraction type
******************************************************/
typedef struct
{
int numerator;
int denominator;
} Fraction;
/*******************************************************
* Name : initF
* Purpose : initialize fraction
* Input : num – numerator
* denum – denominator
* Output : none,
* wasErr() can be called after,
* to test whether the result is correct
* Return : initialized fraction
******************************************************/
Fraction initF(int num, int denum);
/*******************************************************
* Name : isNullF
* Purpose : test fraction to be 0
* Input : f – fraction
* Output : none
* Return : 1 – if is f=0, 0 – if f!=0
*
******************************************************/
int isNullF(Fraction f);
/*******************************************************
* Name : compareF
* Purpose : compares two fractions
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
125

* Input : f1, f2 – two fractions
* Output : none
* Return : 1 – f1<f2,0 – f1=f2, -1 f1<f2
******************************************************/
int compareF(Fraction f1, Fraction f2);
/*******************************************************
* Name : addF
* Purpose : adds fractions
* Input : f1, f2 – two fractions
* Output : none
* Return : fraction (sum of f1 and f2)
******************************************************/
Fraction addF(Fraction f1, Fraction f2);
/*******************************************************
* Name : subF
* Purpose : substracts fractions
* Input : f1, f2 – two fractions
* Output : none
* Return : fraction (substruction of f1 and f2)
******************************************************/
Fraction subF(Fraction f1, Fraction f2);
/*******************************************************
* Name : multF
* Purpose : multiplyes fractions
* Input : f1, f2 – two fractions
* Output : none
* Return : fraction (multiplication of f1 and f2)
*****************************************************/
Fraction multF(Fraction f1, Fraction f2);
/*******************************************************
* Name : divF
* Purpose : divedes fractions
* Input : f1, f2 – two fractions
* Output : none,
* wasErr() can be called after,
* to test whether the result is correct
* Return : fraction (division of f1 and f2)
******************************************************/
Fraction divF(Fraction f1, Fraction f2);
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
126

/*******************************************************
* Name : convertFromIntF
* Purpose : converts int type to fraction
* Input : num – number to convert
* Output : none
* Return : fraction
******************************************************/
Fraction convertFromIntF(int num);
/*******************************************************
* Name : convertToDoubleF
* Purpose : converts fraction to double type
* Input : f – fraction to convert
* Output : none
* Return : double type number
******************************************************/
double convertToDoubleF(Fraction f);
/*******************************************************
* Name : getNumeratorF
* Purpose : returns fraction numerator
* Input : f – fraction to convert
* Output : none
* Return : numerator
******************************************************/
int getNumeratorF(Fraction f);
/*******************************************************
* Name : getDenomimantor
* Purpose : returns fraction denominator
* Input : f – fraction to convert
* Output : none
* Return : denominator
******************************************************/
int getDenomimantor(Fraction f);
/*******************************************************
* Name : wasErr
* Purpose : test whether previous function
* returned correct result
* WORKS ONLY AFTER initF, divF
* Input : none
* Output : none
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
127

* Return : none
******************************************************/
int wasErr();
void printF(Fraction f);
----- файл fraction.h ----#include "fraction.h"
int wasError;
Fraction initF(int num, int denum)
{
Fraction f;
if (denum > 0)
{
f.numerator = num;
f.denominator= denum;
shorten(&f);
wasError = 0;
} else
{
wasError = 1;
}
return f;
}
int isNullF(Fraction f)
{
if (f.numerator == 0)
{
return 1;
} else
{
return 0;
}
}
int compareF(Fraction f1, Fraction f2)
{
Fraction f;
f = subF(f1,f2);
if (f.numerator > 0)
{
return 1;
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
128

} else
{
if (f.numerator < 0)
{
return -1;
} else
{
return 0;
}
}
}
Fraction addF(Fraction f1, Fraction f2)
{
Fraction res;
res.numerator = (f1.numerator*f2.denominator) +
(f2.numerator*f1.denominator);
res.denominator = f1.denominator*f2.denominator;
shorten(&res);
return res;
}
Fraction subF(Fraction f1, Fraction f2)
{
Fraction res;
res.numerator = (f1.numerator*f2.denominator) (f2.numerator*f1.denominator);
res.denominator = f1.denominator*f2.denominator;
return res;
}
Fraction multF(Fraction f1, Fraction f2)
{
Fraction res;
res.numerator = f1.numerator*f2.numerator;
res.denominator = f1.denominator*f2.denominator;
return res;
}
Fraction divF(Fraction f1, Fraction f2)
{
Fraction res;
res.numerator = f1.numerator*f2.denominator;
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
129

res.denominator = f1.denominator*f2.numerator;
if (res.denominator != 0)
{
wasError = 0;
} else
{
wasError = 1;
res.numerator = 0;
res.denominator = 1;
}
return res;
}
Fraction convertFromIntF(int num)
{
Fraction res;
res.numerator = num;
res.denominator = 1;
return res;
}
double convertToDoubleF(Fraction f)
{
return ((double)f.numerator / (double)f.denominator);
}
int getNumeratorF(Fraction f)
{
return f.numerator;
}
int getDenomimantor(Fraction f)
{
return f.denominator;
}
long gcd(long a, long b)
{
a = labs(a);
b = labs(b);
while ((a!=0) && (b!=0))
{
if (a > b)
{
Основы разработки программного обеспечения на примере языка СС.В. Синицын, О.И. Хлытчиев
130
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
