Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
OOP / books / Osnovi objektno-orientirovannogo programmirovaniya.pdf
Скачиваний:
62
Добавлен:
03.03.2016
Размер:
9.04 Mб
Скачать

Работа с утверждениями

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

Класс стек

Поставляемый с утверждениями класс STACK был оставлен пока в схематичной форме ( STACK1 ). Теперь на суд предстанет полная версия, включающая реализацию.

Для написания эффективного класса необходимо задать реализацию. В качестве таковой выберем реализацию стека на базе массива, уже обсуждавшаяся при рассмотрении АТД в шестой лекции.

Рис. 11.2. Реализация стека на базе массива

Массив, названный representation, имеет границы 1 и capacity: реализация использует также целочисленный атрибут count, отмечающий вершину стека. Заметьте, после того, как мы откроем для себя наследование, появится возможность писать классы с отложенной реализацией, что позволит покрывать несколько возможных реализаций, а не одну конкретную. Даже для класса c фиксированной реализацией, например, как здесь на базе массива, мы будем иметь возможность строить его как потомка родительского класса Array. В данный момент никакие методы наследования применяться не будут.

Вот он класс. Остается напомнить, что для массива a операция, присваивающая значение x его i -му элементу, записывается так: a.put(x,i). Получить i -й элемент можно так: a.item(i) или a @ i. Если, как здесь, границы заданы, то i во всех случаях лежит между этими границами: 1<= i <= capacity.

indexing

description: "Стеки: Структуры с политикой доступа Last-In, First-Out % % Последний пришел - Первый ушел, и с фиксированной емкостью"

class STACK2 [G] creation make

feature - Initialization (Инициализация) make (n: INTEGER) is

-- Создать стек, содержащий максимум n элементов require

positive_capacity: n >= 0 do

capacity := n

create representationlmake (1, capacity) ensure

capacity_set: capacity = n array_allocated: representation /= Void stack_empty: empty

end

feature - Access (Доступ) capacity: INTEGER

--Максимальное число элементов стека count: INTEGER

--Число элементов стека

item: G is

--Элемент на вершине стека require

not_empty: not empty -- i.e. count > 0 do

Result := representation @ count end

feature -- Status report (Отчет о статусе) empty: BOOLEAN is

--Пуст ли стек?

do

Result := (count = 0) ensure

empty_definition: Result = (count = 0) end

full: BOOLEAN is

-- Заполнен ли стек? do

Result := (count = capacity) ensure

full_definition: Result = (count = capacity) end

feature -- Element change (Изменение элементов) put (x: G) is

--Добавить элемент x на вершину require

not_full: not full -- т.е. count < capacity в этом представлении do

count := count + 1 representation.put (count, x) ensure

not_empty: not empty added_to_top: item = x

one_more_item: count = old count + 1 in_top_array_entry: representation @ count = x end

remove is

--удалить элемент вершины стека

require

not_empty: not empty -- i.e. count > 0 do

count := count - 1 ensure

not_full: not full

one_fewer: count = old count - 1 end

feature {NONE} -- Implementation (Реализация) representation: ARRAY [G]

-- Массив, используемый для хранения элементов стека invariant

... Будет добавлен позднее ...

end

Текст класса иллюстрирует простоту работы с утверждениями. Это полный текст, за исключением предложений invariant, задающих инварианты класса, которые будут добавлены позднее в этой лекции. Давайте исследуем различные свойства класса.

Это первый законченный класс этой лекции, не слишком далеко отличающийся от того, что можно найти в профессиональных библиотеках повторно используемых ОО-компонентов, таких как Базовые библиотеки. Одно замечание о структуре класса. Поскольку класс имеет более двухтрех компонентов, возникает необходимость сгруппировать его компоненты подходящим образом. Нотация позволяет реализовать такую возможность введением множества предложений feature. Это свойство группировки компонентов, введенное в предыдущей лекции, использовалось там, как способ задания различного статуса экспорта компонентов. И здесь в последней части класса, помеченной Implementation, это свойство используется для указания защищенности компонента representation. Но преимущества группирования можно использовать и при неизменном статусе экспорта. Его цель - сделать класс простым при чтении и легче управляемым, группируя компоненты по категориям. После каждого ключевого слова feature появляется комментарий, называемый комментарий к предложению Feature (Feature Clause Comment). Он позволяет дать содержательное описание данной категории - роль компонентов, включенных в этот раздел. Категории, используемые в примере, те же, что и при описании класса STACK1 с добавлением раздела Initialization с процедурой создания (конструктором).

Стандартные категории feature и связанные с ними комментарии к предложениям Feature являются частью общих правил организации повторно используемых библиотек классов.

Императив и аппликатив (применимость)

Утверждения из STACK2 иллюстрируют фундаментальную концепцию - разницу между императивным и аппликативным видением.

Утверждения empty и full могут вызвать удивление. Приведу еще раз текст full:

full: BOOLEAN is

-- Заполнен ли стек? do

Result := (count = capacity) ensure

full_definition: Result = (count = capacity) end

Постусловие говорит, что Result имеет значение выражения ( count = capacity ). Но оператор присваивания именно это значение присваивает переменой Result. В чем же смысл написания постусловия? Не является ли оно избыточным?

Фактически между двумя конструкциями большая разница. Присваивание это команда, отданная виртуальному компьютеру на изменение его состояния. Утверждение ничего не делает, оно специфицирует свойство ожидаемого заключительного состояния, полученное клиентом, вызвавшим программу.

Инструкция предписывает ( prescriptive ), утверждение описывает ( descriptive ). Инструкция описывает "как", утверждение описывает "что". Инструкция является частью реализации, утверждение - элементом спецификации.

Инструкция императивна, утверждение - аппликативно. Эти два термина выражают

фундаментальную разницу между программированием и математикой.

*Компьютерные операции могут изменять состояние аппаратно-программной машины. Инструкции в языках программирования являются командами (императивные конструкции), заставляющие машину выполнять такие операции.

*Математические вычисления никогда ничего не меняют. Как отмечалось при рассмотрении АТД, взятие квадратного корня от числа 2 не меняет это число. Вместо этого математики описывают как, используя свойства одних объектов, вывести свойства других, таких как v2, полученных применением ( applying - отсюда и термин "аппликативный") математических трансформаций.

То, что две нотации в нашем примере так близки, - присваивание и эквивалентность - не должно затемнять фундаментальное различие. Утверждение описывает ожидаемый результат, инструкция предписывает способ его достижения. Клиенты модуля обычно интересуются утверждениями, а не реализациями.

Причина близости нотаций в том, что присваивание зачастую кратчайший путь достижения эквивалентности. Но при переходе к более сложным примерам концептуальное различие между спецификацией и реализацией будет только возрастать. Даже в простейшем случае вычисления квадратного корня постусловие может быть задано в форме: abs(Result^2 -x) <= tolerance, где abs - обозначает абсолютное значение, а tolerance - допустимое отклонение от точного значения. Инструкции, вычисляющие квадратный корень, могут быть не тривиальными, реализуя определенный алгоритм вычисления квадратного корня.

Даже для put в классе STACK2 одной и той же спецификации могут соответствовать различные алгоритмы, например:

if count = capacity then Result := True else Result := False end

или упрощенный вариант, учитывающий правила инициализации:

if count = capacity then Result := True end

Входе работы мы столкнулись со свойством утверждений, заслуживающим дальнейшей проработки: оно важно для авторов клиентских классов, не интересующихся реализацией, но нуждающихся в абстрактном описании роли программы. Эта идея приведет нас к понятию краткой формы (short form), обсуждаемой далее в этой лекции в качестве основного механизма документирования класса.

Предупреждение: по практическим соображениям допускается включение в утверждение функций - по внешнему виду императивных элементов. Эта проблема исследуется в конце этой лекции.

Взаключение обсуждения полезно перечислить слова, используемые по контрасту в двух категориях программных элементов:

|Реализация | Спецификация | |Инструкция | Выражение | |Как | Что | |Императив | Аппликатив |

|Предписание | Описание | Таблица 11.2.Императивно - аппликативное противопоставление

Соседние файлы в папке books