Инвариант класса
Эта статья нуждается в дополнительных ссылок для проверки . ( август 2010 г. ) |
В компьютерном программировании , особенно в объектно-ориентированном программировании , инвариант класса (или инвариант типа ) — это инвариант, для ограничения объектов класса используемый . Методы класса должны сохранять инвариант. Инвариант класса ограничивает состояние, хранящееся в объекте.
Инварианты классов устанавливаются во время создания и постоянно поддерживаются между вызовами общедоступных методов. Код внутри функций может нарушать инварианты, если инварианты восстанавливаются до завершения публичной функции. При параллелизме сохранение инварианта в методах обычно требует создания критической секции путем блокировки состояния с помощью мьютекса .
или Инвариант объекта инвариант представления — это конструкция компьютерного программирования, состоящая из набора инвариантных свойств, которые остаются неизменными независимо от состояния объекта. Это гарантирует, что объект всегда будет соответствовать предопределенным условиям и, следовательно, методы всегда могут ссылаться на объект без риска сделать неточные предположения. Определение инвариантов классов может помочь программистам и тестировщикам выявлять больше ошибок во время тестирования программного обеспечения .
Инварианты классов и наследование [ править ]
Полезный эффект инвариантов классов в объектно-ориентированном программном обеспечении усиливается при наличии наследования. Инварианты классов наследуются, то есть «инварианты всех родителей класса применяются к самому классу». [1]
Наследование может позволить классам-потомкам изменять данные реализации родительских классов, поэтому класс-потомок может изменить состояние экземпляров таким образом, что они станут недействительными с точки зрения родительского класса. Забота о таком типе неправильного поведения потомков является одной из причин, по которой разработчики объектно-ориентированного программного обеспечения отдают предпочтение композиции, а не наследованию (т. е. наследование нарушает инкапсуляцию). [2]
Однако, поскольку инварианты классов наследуются, инвариант класса для любого конкретного класса состоит из любых инвариантных утверждений, закодированных непосредственно в этом классе, в сочетании со всеми инвариантными предложениями, унаследованными от родителей класса. Это означает, что даже несмотря на то, что классы-потомки могут иметь доступ к данным реализации своих родителей, инвариант класса может помешать им манипулировать этими данными любым способом, который приведет к созданию недопустимого экземпляра во время выполнения.
Поддержка языков программирования [ править ]
Утверждения [ править ]
Распространенные языки программирования, такие как Python, [3] PHP, [4] JavaScript, [ нужна цитата ] C++ и Java по умолчанию поддерживают утверждения , которые можно использовать для определения инвариантов классов. Распространенным шаблоном реализации инвариантов в классах является то, что конструктор класса генерирует исключение, если инвариант не удовлетворяется. Поскольку методы сохраняют инварианты, они могут предполагать допустимость инварианта и не нуждаются в его явной проверке.
Встроенная поддержка [ править ]
Инвариант класса является важным компонентом проектирования по контракту . Таким образом, языки программирования, которые обеспечивают полную встроенную поддержку проектирования по контракту , такие как Eiffel , Ada и D , также будут обеспечивать полную поддержку инвариантов классов.
Неродная поддержка [ править ]
Для C++ библиотека Loki предоставляет основу для проверки инвариантов классов, инвариантов статических данных и безопасности исключений.
Для Java существует более мощный инструмент под названием Java Modeling Language , который обеспечивает более надежный способ определения инвариантов классов.
Примеры [ править ]
Встроенная поддержка [ править ]
Есть [ править ]
Язык программирования Ada имеет встроенную поддержку инвариантов типов (а также пред- и постусловий, предикатов подтипов и т. д.). Инвариант типа может быть задан для частного типа (например, для определения связи между его абстрактными свойствами) или для его полного определения (обычно для помощи в проверке правильности реализации типа). [5] Вот пример инварианта типа, заданного в полном определении частного типа, используемого для представления логического стека. Реализация использует массив, а инвариант типа определяет определенные свойства реализации, которые обеспечивают доказательство безопасности. В этом случае инвариант гарантирует, что для стека логической глубины N первые N элементов массива являются допустимыми значениями. Default_Initial_Condition типа Stack, указывая пустой стек, обеспечивает начальную истинность инварианта, а Push сохраняет инвариант. Тогда истинность инварианта позволяет Попу полагаться на тот факт, что вершина стека является допустимым значением, что необходимо для доказательства постусловия Попа. Более сложный инвариант типа позволил бы доказать полную функциональную корректность, например, что Pop возвращает значение, переданное в соответствующий Push, но в этом случае мы просто пытаемся доказать, что Pop не возвращает Invalid_Value.
общий
тип Item is Private ;
Invalid_Value : в элементе ;
пакет Stacks имеет
тип Stack ( Max_Depth : Positive ) является частным
с Default_Initial_Condition => Is_Empty ( Stack );
функция Is_Empty ( S : in Stack ) возвращает логическое значение ;
функция Is_Full ( S : in Stack ) возвращает логическое значение ;
процедура Push ( S : in out Stack ; I : in Item )
с Pre => not Is_Full ( S ) , а затем I /= Invalid_Value ,
Post => not Is_Empty ( S );
процедура Pop ( S : in out Stack ; I : out Item )
с Pre => not Is_Empty ( S ),
Post => not Is_Full ( S ) , а затем I /= Invalid_Value ;
частный
тип Item_Array представляет собой ( положительный диапазон < >) элемента массив ;
тип Stack ( Max_Depth : Positive ) — записи
длина : Natural := 0 ;
Данные : Item_Array ( 1 .. Max_Depth ) : = ( другие => Invalid_Value );
завершить запись
с помощью Type_Invariant => Длина <= Max_Depth , а затем
( для всех J в 1 .. Длина => Данные ( J ) /= Invalid_Value );
функция Is_Empty ( S : в стеке ) возвращает логическое
значение ( S. Длина = ; 0 )
функция Is_Full ( S : в стеке ) return Boolean
is ( S . Длина = S . Max_Depth );
конец стеков ;
Д [ править ]
Язык программирования D имеет встроенную поддержку инвариантов классов, а также других методов контрактного программирования . Вот пример из официальной документации. [6]
класса дата {
int день ;
интервал часа ;
инвариант () {
утверждения ( день >= 1 && день <= 31 );
утверждать ( час >= 0 && час <= 23 );
}
}
Эйфелева [ править ]
В Eiffel инвариант класса появляется в конце класса после ключевого слова invariant
.
class
DATE
create
make
Feature { NONE } -- Инициализация
make ( a_day : INTEGER ; a_hour : INTEGER )
-- Инициализируйте `Current' с `a_day' и `a_hour'.
требуется
valid_day : a_day >= 1 и a_day <= 31
valid_hour : a_hour = 0 и a_hour <= 23
do
day := :
a_dayhour_set = a_hour
обеспечения
day_set : day = >
: hour a_dayhour = a_hour
end
Feature доступа
-- День : INTEGER
— День месяца для «Текущего»
часа : INTEGER
— Час дня для «Текущей»
функции — Изменение элемента
set_day ( a_day : INTEGER )
— Установите для «day» значение «a_day»
требуется
valid_argument : a_day >= 1 and a_day <= 31
do
day := a_day
обеспечения
day_set : day = a_day
end
set_hour ( a_hour : INTEGER )
-- Установите `hour' в `a_hour'
require
valid_argument : a_hour >= 0 и a_hour <= 23
dohour
: = a_hour
обеспечить
часов_set : час = а_час
конец
инвариант
действительный_день : день >= 1 и день <= 31
действительный_час : час >= 0 и час <= 23
конец
Неродная поддержка [ править ]
С++ [ править ]
Библиотека Loki (C++) предоставляет платформу, написанную Ричардом Спосато, для проверки инвариантов классов, инвариантов статических данных и уровня безопасности исключений .
Это пример того, как класс может использовать Loki::Checker для проверки того, что инварианты остаются верными после изменения объекта. В примере используется объект geopoint для хранения местоположения на Земле в виде координат широты и долготы.
Инварианты геоточки:
- широта не может быть выше 90° северной широты.
- широта не может быть ниже -90° южной широты.
- долгота не может быть более 180° восточной долготы.
- долгота не может быть меньше -180° западной долготы.
#include <loki/Checker.h> // Требуется для проверки инвариантов класса.
#include <Degrees.hpp>
class GeoPoint {
public :
GeoPoint ( в градусах широта , в градусах долгота );
/// Функция Move переместит местоположение GeoPoint.
void Move ( Degrees latitude_change , Degrees longitude_change ) {
// Объект проверки вызывает IsValid при входе и выходе из функции, чтобы доказать, что этот
// объект GeoPoint действителен. Средство проверки также гарантирует, что
функция GeoPoint::Move // никогда не выдаст ошибку.
CheckFor :: CheckForNoThrow Проверка ( this , & IsValid );
широта_ += широта_изменение ;
если ( широта_ >= 90,0 ) широта_ = 90,0 ;
если ( широта_ <= -90,0 ) широта_ = -90,0 ;
долгота_ += изменение долготы ;
while ( longitude_ >= 180,0 ) longitude_ -= 360,0 ;
while ( longitude_ <= -180,0 ) longitude_ += 360,0 ;
}
Private :
/** @note CheckFor выполняет проверку правильности во многих функциях, чтобы определить,
не нарушил ли код какие-либо инварианты, не изменилось ли какое-либо содержимое или не
выдала ли функция исключение.
*/
используя CheckFor = :: Loki :: CheckFor < const GeoPoint > ;
/// Эта функция проверяет все инварианты объекта.
bool IsValid () const {
утверждать ( это != nullptr );
утверждать ( широта_ >= -90,0 );
утверждать ( широта_ <= 90,0 );
утверждать ( longitude_ >= -180,0 );
утверждать ( longitude_ <= 180,0 );
вернуть истину ;
}
Градусы широты_ ; ///< Градусы от экватора. Положительное значение — север, отрицательное —
///< юг.
Градусы долготы_ ; ///< Градусы от нулевого меридиана. Положительное значение — восток,
///< отрицательное — запад.
}
Ява [ править ]
Это пример инварианта класса в языке программирования Java с помощью языка моделирования Java . Инвариант должен оставаться истинным после завершения конструктора, а также при входе и выходе всех открытых членов. функции. Открытые функции-члены должны определять предусловие и постусловие , чтобы гарантировать инвариантность класса.
публичный класс Date {
int /*@spec_public@*/ день ;
int /*@spec_public@*/ час ;
/*@invariant day >= 1 && day <= 31; @*/ //инвариант класса
/*@invarianthour >= 0 &&hour <= 23; @*/ //инвариант класса
/*@
@requires d >= 1 && d <= 31;
@requires h >= 0 && h <= 23;
@*/
public Date ( int d , int h ) { конструктора
// день = d ;
час = час ;
}
/*@
@requires d >= 1 && d <= 31;
@обеспечивает день == d;
@*/
public void setDay ( int d ) {
day = d ;
}
/*@
@requires h >= 0 && h <= 23;
@обеспечивает час == час;
@*/
public void setHour ( int h ) {
hour = h ;
}
}
Ссылки [ править ]
- ^ Мейер, Бертран. Объектно-ориентированное построение программного обеспечения , второе издание, Prentice Hall, 1997, стр. 570.
- ^ Э. Гамма, Р. Хелм, Р. Джонсон и Дж. Влиссидес. Шаблоны проектирования: элементы объектно-ориентированного программного обеспечения многократного использования . Аддисон-Уэсли, Ридинг, Массачусетс, 1995 г., с. 20.
- ^ Официальная документация Python, утверждение утверждения
- ^ «Функция утверждения PHP» . Архивировано из оригинала 21 марта 2001 г.
- ^ «Справочное руководство Ada 7.3.2 Инварианты типов» . ada-auth.org . Проверено 27 ноября 2022 г.
- ^ «Контрактное программирование — язык программирования D» . dlang.org . Проверено 29 октября 2020 г.