Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции по основам ООП.docx
Скачиваний:
0
Добавлен:
27.12.2019
Размер:
5.29 Mб
Скачать

Инварианты класса

Предусловия и постусловия описывают свойства отдельных программ. Но экземпляры класса обладают также глобальными свойствами. Их принято называть инвариантами класса (class invariants), и они определяют более глубокие семантические свойства и ограничения целостности, характеризующие класс.

Определение и пример

Рассмотрим снова реализацию стека классом STACK2 :

class STACK2 [G] creation

make

feature

? make, empty, full, item, put, remove?

capacity: INTEGER

count: INTEGER

feature NONE -- Implementation

representation: ARRAY [G]

end

Атрибуты класса - массив representation и целые capacity , count - задают представление стека. Хотя предусловия и постусловия программ отражают семантику стека, их недостаточно для выражения важных свойств, связывающих атрибуты. Например, count всегда должно удовлетворять условию:

0 <= count; count <= capacity

из которого следует, что capacity >=0 и что capacity задает размер массива:

capacity = representation.capacity

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

Выше приведенные примеры инвариантов включали только атрибуты. Инварианты могут выражать отношения между функциями и между функциями и атрибутами. Например, инвариант STACK2 может включать следующее свойство, описывающее связь между функцией empty и count :

empty = (count = 0)

Этот пример не показателен, он повторяет утверждение, заданное постусловием empty . Более полезные утверждения те, которые включают только атрибуты или более чем одну функцию.

Вот еще один типичный пример. Предположим, что мы имеем дело с банковскими счетами, и есть класс Bank_Account с компонентами: deposits_list , withdrawals_list и balance . Тогда инвариантом такого класса может быть утверждение в форме:

consistent_balance: deposits_list.total - withdrawals_list.total = balance

где функция total дает суммарное значение списка всех операций (приходных или расходных). Инвариант определяет основное условие согласования всех банковских операций над счетом, связывая баланс, приходные и расходные операции.

Форма и свойства инвариантов класса

Синтаксически инвариант класса является утверждением, появляющимся в предложении invariant , стоящим после всех предложений feature , и перед предложением end . Вот пример:

class STACK4 [G] creation

...Как в STACK2...

feature

...Как в STACK2...

invariant

count_non_negative: 0 <= count

count_bounded: count <= capacity

consistent_with_array_size: capacity = representation.capacity

empty_if_no_elements: empty = (count = 0)

item_at_top: (count > 0) implies (representation.item (count) = item)

end

Инвариант класса C это множество утверждений, которым удовлетворяет каждый экземпляр класса во все "стабильные" времена. В эти времена экземпляр класса находится в наблюдаемом состоянии:

[x]. на момент создания экземпляра, сразу после выполнения create a или create a.make(...) , где a класса C ;

[x]. перед и после каждого удаленного вызова a.r(...) программы r класса С .

Следующий рисунок, показывающий жизнь объектов, поможет разобраться в инвариантах и стабильных временах:

Рис. 11.4.  Жизнь объектов

Жизнь объектов не столь уж захватывающая. Вначале - слева на рисунке - он просто не существует. При выполнении инструкции create a или create a.make(...) или clone объект создается и достигает первой станции S1 в своей жизни. Затем идет череда довольно скучных событий: клиенты, для которых доступен объект, один за другим вызывают его компоненты в форме a.f(..) . Так все продолжается, пока не завершится вычисление.

Инвариант является характеристическим свойством состояний, представленных большими квадратиками на рисунке: S1, S2, S3 и т.д. Эти состояния соответствуют стабильным временам, упомянутым выше.

Здесь рассматриваются последовательные вычисления, но все идеи легко переносятся на параллельные вычисления, что и будет сделано в соответствующей лекции.