Что такое инвариант в ООП?
Что имеется ввиду под этим термином? Как выглядят инварианты в коде?
Я нашёл описание термина «инвариант цикла»:
Инвариант цикла – это соотношение, которое истинно перед циклом, истинно в процессе выполнения цикла и истинно при выходе из цикла. Все это описано у Дейкстры в книге «Дисциплина программирования», и детально разжевано у Гриса в книге «Наука программирования».
А хотелось бы понять, что понимают под инвариантом
- в программировании по контракту и
- чистом ООП (я так понял, это имеет отношение к инкапсуляции)
Отслеживать
80.9k 9 9 золотых знаков 78 78 серебряных знаков 134 134 бронзовых знака
задан 21 июл 2018 в 21:06
28.6k 19 19 золотых знаков 59 59 серебряных знаков 139 139 бронзовых знаков
4 ответа 4
Сортировка: Сброс на вариант по умолчанию
Инвариант в математике — это выражение которое сохраняет свое значение. В программировании инвариантом также называют предикат который всегда истинный.
Таким образом, инвариант объекта в ООП — это либо (чаще) условие которое остается истинным после вызова любых методов объекта в любой последовательности, либо (реже) выражение которое сохраняет свое значение после вызова любых методов.
В коде инварианты чаще всего никак не выражены, но иногда ставятся защитные проверки которые их проверяют.
List<> : 0 ≤ _size ≤ _items.Length
List<>.Enumerator : list.version = const = version; есть защитная проверка
12.2.7.1 Инварианты.
Значение членов или объектов, доступных с помощью членов класса, называется состоянием объекта (или просто значением объекта). Главное при построении класса — это: привести объект в полностью определенное состояние (инициализация), сохранять полностью определенное состояние обЪекта в процессе выполнения над ним различных операций, и в конце работы уничтожить объект без всяких последствий. Свойство, которое делает состояние объекта полностью определенным, называется инвариантом.
Поэтому назначение инициализации — задать конкретные значения, при которых выполняется инвариант объекта. Для каждой операции класса предполагается, что инвариант должен иметь место перед выполнением операции и должен сохраниться после операции. В конце работы деструктор нарушает инвариант, уничтожая объект. Например, конструктор String::String( const char *) гарантирует, что p указывает на массив из, по крайней мере, sz элементов, причем sz имеет осмысленное значение и v[sz-1]==0. Любая строковая операция не должна нарушать это утверждение.
При проектировании класса требуется большое искусство, чтобы сделать реализацию класса достаточно простой и допускающей наличие полезных инвариантов, которые несложно задать. Легко требовать, чтобы класс имел инвариант, труднее предложить полезный инвариант, который понятен и не накладывает жестких ограничений на действия разработчика класса или на эффективность реализации. Здесь «инвариант» понимается как программный фрагмент, выполнив который, можно проверить состояние объекта. Вполне возможно дать более строгое и даже математическое определение инварианта, и в некоторых ситуациях оно может оказаться более подходящим. Здесь же под инвариантом понимается практическая, а значит, обычно экономная, но неполная проверка состояния объекта.
Понятие инварианта появилось в работах Флойда, Наура и Хора, посвященных пред- и пост-условиям, оно встречается во всех важных статьях по абстрактным типам данных и верификации программ за последние 20 лет. Оно же является основным предметом отладки в C++.
Обычно, в течение работы функции-члена инвариант не сохраняется. Поэтому функции, которые могут вызываться в те моменты, когда инвариант не действует, не должны входить в общий интерфейс класса. Такие функции должны быть частными или защищенными.
Как можно выразить инвариант в программе на С++? Простое решение — определить функцию, проверяющую инвариант, и вставить вызовы этой функции в общие операции. Например:
class String < int sz; int* p; public: class Range <>; class Invariant <>; void check(); String(const char* q); ~String(); char& operator[](int i); int size() < return sz; > //. >; void String::check() < if (p==0 || szthrow Invariant; > char& String::operator[](int i) < check(); // проверка на входе if (ithrow Range; // действует check(); // проверка на выходе return v[i]; >
Этот вариант прекрасно работает и не осложняет жизнь программиста. Но для такого простого класса как String проверка инварианта будет занимать большую часть времени счета. Поэтому программисты обычно выполняют проверку инварианта только при отладке:
inline void String::check() < if (!NDEBUG) if (p==0 || szthrow Invariant; >
Мы выбрали имя NDEBUG, поскольку это макроопределение, которое используется для аналогичных целей в стандартном макроопределении С assert(). Традиционно NDEBUG устанавливается с целью указать, что отладки нет. Указав, что check() является подстановкой, мы гарантировали, что никакая программа не будет создана, пока константа NDEBUG не будет установлена в значение, обозначающее отладку. С помощью шаблона типа Assert() можно задать менее регулярные утверждения, например:
templateclass T, class X> inline void Assert(T expr,X x) < if (!NDEBUG) if (!expr) throw x; >
вызовет особую ситуацию x, если expr ложно, и мы не отключили проверку с помощью NDEBUG. Использовать Assert() можно так:
class Bad_f_arg < >; void f(String& s, int i) < Assert(0
Шаблон типа Assert() подражает макрокоманде assert() языка С. Если i не находится в требуемом диапазоне, возникает особая ситуация Bad_f_arg.
С помощью отдельной константы или константы из класса проверить подобные утверждения или инварианты - пустяковое дело. Если же необходимо проверить инварианты с помощью объекта, можно определить производный класс, в котором проверяются операциями из класса, где нет проверки, см. упр.8 в $$13.11.
Для классов с более сложными операциями расходы на проверки могут быть значительны, поэтому проверки можно оставить только для "поимки" трудно обнаруживаемых ошибок. Обычно полезно оставлять по крайней мере несколько проверок даже в очень хорошо отлаженной программе. При всех условиях сам факт определения инвариантов и использования их при отладке дает неоценимую помощь для получения правильной программы и, что более важно, делает понятия, представленные классами, более регулярными и строго определенными. Дело в том, что когда вы создаете инварианты, то рассматриваете класс с другой точки зрения и вносите определенную избыточность в программу. То и другое увеличивает вероятность обнаружения ошибок, противоречий и недосмотров. Мы указали в $$11.3.3.5, что две самые общие формы преобразования иерархии классов состоят в разбиении класса на два и в выделении общей части двух классов в базовый класс. В обоих случаях хорошо продуманный инвариант может подсказать возможность такого преобразования. Если, сравнивая инвариант с программами операций, можно обнаружить, что большинство проверок инварианта излишни, то значит класс созрел для разбиения. В этом случае подмножество операций имеет доступ только к подмножеству состояний объекта. Обратно, классы созрели для слияния, если у них сходные инварианты, даже при некотором различии в их реализации.
Оформление и дизайн книги OtDiatlovaOU.
Вся книга, архив.
Программирование, блок схема, программа, информатика, алгоритм, управление, система управления, разделяй властвуй, языки программирования, линейное программирование, сложность, книги программирование, организация, развитие, проектирование, самосовершенствование, развитие систем, программирование скачать, программирование c, задачи программирование, динамическое программирование, ориентированное программирование, методы программирования, объектно программирование, примеры программирования, задача линейного программирования, основы программирования, объектно ориентированное программирование, программирование учебник, технология программирования, программирование си, программирование скачать книги, исходники, исходники на c, c, с, програмирование, книги по с, разработка программ, государственное управление, методы управления, управление организацией, структуры управления, управление проектами, управление рисками, теории управления, скачать управление, процесс управления, исследование управления, программа управления, схемы управления, информационное управление, управление образования, стратегическое управление, исследование систем управления, социальное управление, функции управления, технология управления, модели управления, блок управления, организационное управление, менеджмент управления, психология управления, управление ресурсами, управление производством, принципы управления, корпоративное управление, управление работами, дистанционное управление, эффективность управления, управление компьютером, пульты управления, проблемы управления, области управления, основы управления, управление конфликтами, обеспечение управления, управление деятельностью, анализ управления, автоматизированное управление, стили управления, организационные структуры управления, автоматическое управление, современное управление, подходы управления, управление службами, стратегии управления, социология управления, управление развитием, объект управления, информационные технологии управления, автоматизированные системы управления, совершенствование управления, управление средствами, управление потоками, оперативное управление, механизм управления, управление удаленным, примеры управления, управление через, понятие управление, особенности управления, задачи управления, сфера управления, управление культуры, право управления, управление собственностью, управление книги, концепция управления, управление трудом, панель управления, опыт управления, информационные системы управления, формы управления, роль управления, политика управления, контроль управления, организация, управление организацией, организация труда, организация производства, теория организации, организация система, организация учета, структура организации, формы организации, анализ организации, принципы организации, организация процессов, развитие организации, пример организации , среда организации, организация контроля, метод организации, внутренняя организация, стратегии организации, понятие организация, уровни организации, основы организации, функции организации, современная организация, организация проекта, развитие, перспективы развития, этапы развития, программа развития, тенденции развития, развитие систем, стратегия развития, проблемы развития, концепция развития, развитие личности, дети развитие, современное развитие, развитие ребенка, план развития, теория развития, психология развития, особенности развития, развитие техники, развитие человека, развитие образования, устойчивое развитие, развитие памяти, фактор развития, развитие способностей, управление развитием, развитие связи, развитие технологии, развитие мышления, направления развития, пути развития, модели развития, русское развитие, развитие жизни, основные этапы развития, развитие страны, возникновение развитие, стратегическое развитие, развитие информационного развития, скачать развитие, развитие персонала, методы развития, творческое развитие, развитие языка, развитие школьника, проектирование, проектирование систем, проектирование программа, нормы проектирования, проектирование информационных, проектирование данных, проектирование информационной системы, проектирование базы, организационное проектирование, основы проектирования, автоматизированное проектирование, проектирование скачать, организация проектирования, методы проектирования, управление проектирование, технологическое проектирование, проектирование процессов, этапы проектирования, системы автоматизированного проектирования
Отличие терминов Инварианта и Свойства?
Чем отличается понятия инварианта и свойства (неизменного)?
( Корректно ли составлен вопрос?
В чем смысл различия инварианта и спецификации, предъявляемой к классу, который воплощает некую абстракцию из предметной области?
)
Привожу ниже примеры и, возможно, ошибочные суждения:
1) Допустим есть граф, который можно визуально отобразить разными способами (список связей, матрица вершин, перечисление цепочек). Инвариантом графа является ( или корректнее - являЮтся? ) количество вершин и ребер.
- Инвариантом класса уровня наследования 1 является неприватный интерфейс базового класса.
- Инвариантом класса уровня наследования 2 является неприватный интерфейс конкретного класса-родителя из уровня наследования 1.
3) Допустим, есть контейнер с неотсортированными элементами. Есть класс, конструктор которого принимает контейнер, и есть метод, реализующий сортировку и вызываемый в конструторе. Тогда корректно утверждать, что класс обеспечивает инвариант для отсортированного контейнера.
4) "Куча имеет свойство упорядоченности элементов по невозрастанию" (эквивалентно) "Класс, реализующий абстрактную структуру данных куча, обеспечивает инвариант упорядоченности элементов по невозрастанию"
Пожалуйста, разъясните допущенные ошибки, я сомневаюсь, что я правильно понял концепцию.
Спасибо за внимание.
- Вопрос задан более трёх лет назад
- 1297 просмотров
Комментировать
Решения вопроса 1
Developer, ex-admin
Из википедии:
Инвариант в объектно-ориентированном программировании — выражение, определяющее непротиворечивое внутреннее состояние объекта.
Но это относится не только к ООП, а в принципе.
Из ваших примеров, по моему, только 4 про инвариант.
Исходя из определения из вики, инвариант - логическое выражение (а не свойство), которое всегда должно выполняться для объекта. Если оно не выполняется, то объект (класс, структура и т.п.) находится в несогласованном состоянии и его дальнейшее использование опасно.
Обычно инварианты нарушаются внутри методов класса, при выходе из метода инвариант должен снова восстанавливаться, иначе метод отработал не правильно.
Простой пример: для Си строки инвариант - не нулевой указатель на начало и символ 0 в конце строки.
Например у вас есть функция AddString, которая добавляет к существующей строке другую строку. Для простоты предположим, что не нужно перевыделять память. В процессе выполнения AddString инвариант нарушается - 0 символ заменяется добавляемыми символами второй строки. Но после добавления нулевой символ должен быть восстановлен в конце, после чего инвариант снова будет истинным.
Для одного объекта может быть несколько инвариантов, для разных методов объекта могут быть важны не все инварианты. Если метод в ходе своей работы нарушает какой-то инвариант, есть смысл в начале и конце метода проверить этот инвариант с помощью assertа. Это предотвратит возможные ошибки.
Что такое инвариант в программировании
Честно несколько раз прочитал определение у Страуструпа, но смысл сего понятия от меня ускользает.
Пожалуйста объясните, чем являтся инвариант класса с точки зрения практического кода.
Что это — функция? Как она используется? И т.п.
Re: Что такое Инвариант?
От: | korzhik | |
Дата: | 11.05.05 12:27 | |
Оценка: | 6 (1) |
Здравствуйте, Ignoramus, Вы писали:
I>Честно несколько раз прочитал определение у Страуструпа, но смысл сего понятия от меня ускользает.
I>Пожалуйста объясните, чем являтся инвариант класса с точки зрения практического кода.
I>Что это — функция? Как она используется? И т.п.
здесь помоему понятно написано
Re: Что такое Инвариант?
От: | Mr.Chipset | http://merlinko.com |
Дата: | 11.05.05 12:54 | |
Оценка: | 1 (1) |
Здравствуйте, Ignoramus, Вы писали:
I>Честно несколько раз прочитал определение у Страуструпа, но смысл сего понятия от меня ускользает.
I>Пожалуйста объясните, чем являтся инвариант класса с точки зрения практического кода.
Инвариант — это некоторое логическое условие, значение которого (истина или ложь) должно сохраняться.
Приложительно к ЯП — это набор условий для класса которым должен следовать любой класс в момент вызова. Проще говоря — много инвариантов для методов класса.
Ты юзаешь инварианты классов в повседневном программировании. К примеру, есть класс vector — дин. массив. Логически понятно что номер текущего элемента не должен быть больше общего количества элементов и не должен быть меньше нуля. Ты это описываешь в описании класса:
Ок, теперь девелопер знает что это должно быть истинным для любого обьекта класса, что он и задает в инварианте класса:
class vectorInv < public: bool check(vector bI) < if(bI.cur_elem > bI.count || bI.cur_elem < 0) return false; > >;
И где-нибудь ты намереваешься проверить — нормален ли класс:
ASSERT(check(someObj));
Если условие не выполнено — всё, кранты — ошибка ибо нормальный код должен соответствовать контракту.
Это очень упрощенно и имхо, ещё ведь есть всякие TDD (cppUnit, Boost::Test) и т.д.
I>Что это — функция? Как она используется? И т.п.
"Всё что не убивает нас, делает нас сильнее. "
Re[2]: Что такое Инвариант?
От: | Кодт | |
Дата: | 11.05.05 19:19 | |
Оценка: | 2 (2) |
Здравствуйте, Mr.Chipset, Вы писали:
MC>Отсюда:
MC>
Инвариант — это некоторое логическое условие, значение которого (истина или ложь) должно сохраняться.
MC>Приложительно к ЯП — это набор условий для класса которым должен следовать любой класс в момент вызова. Проще говоря — много инвариантов для методов класса.
Это ты не смешиваешь ли инвариант с предусловием?
Инвариант класса — это утверждение, которое (должно быть) истинно применительно к любому объекту данного класса в любой момент времени (за исключением переходных процессов в методах объекта).
Some x,y,z; assert( SomeInvariant(x) && SomeInvariant(y) && SomeInvariant(z) ); x.do_something(); assert( SomeInvariant(x) && SomeInvariant(y) && SomeInvariant(z) ); do_something_with(y); assert( SomeInvariant(x) && SomeInvariant(y) && SomeInvariant(z) ); interact(x,z); assert( SomeInvariant(x) && SomeInvariant(y) && SomeInvariant(z) );
Например, инвариантом std::vector являются:
— 0 — все элементы в диапазоне [0;size()-1] являются живыми (сконструированными и не разрушенными) объектами
Предусловие — это утверждение, которое является требованием для начала работы (при вызове метода, при входе в блок и т.п.)
Some::method(args) < assert( SomePrecondition(args && this->state && global::state) ); . >
Например, предусловие std::vector::operator[](int index) — индекс должен лежать в диапазоне [0;size()-1]
Постусловие — это утверждение, истинное по окончании работы (возврат из метода, выход из блока и т.п.)
Some x; result = x.method(args); assert( PostCondition(result) );
Постусловие для std::vector::operator[] — результат является ссылкой на тот самый элемент (если индекс валидный).
Для std::vector::at() — либо результат это ссылка на существующий элемент, либо бросок исключения при невалидном индексе.
Естественно, что одним из постусловий является сохранение инварианта.
Перекуём баги на фичи!
Re: Что такое Инвариант?
От: | _doctor | http://agilesoftwaredevelopment.com |
Дата: | 11.05.05 19:42 | |
Оценка: | 1 (1) |
Здравствуйте, Ignoramus, Вы писали:
I>Честно несколько раз прочитал определение у Страуструпа, но смысл сего понятия от меня ускользает.
I>Пожалуйста объясните, чем являтся инвариант класса с точки зрения практического кода.
I>Что это — функция? Как она используется? И т.п.
Как говорит нам gramota.ru, инвариант — это "элемент системы, остающийся неизменным при всех ее модификациях".
Если человеческим языком, то:
У большинства классов присутствует множество членов-данных, которые периодически изменяются. В результате ошибок в программе можно наизменять такого, что объект будет представлять собой полную бессмыслицу. Функия-инвариант проверяет целостность объекта, имеет ли он смысл. Например, для объекта моделирующего человека, инвариант может проверить существование не более, чем двух рук и не более, чем трёх ног
Используется для ловли ошибок. Например в конце каждой сложной/неоднозначной функции можно вызвать инвариант, просто чтобы быть хоть немного уверенным, что объект не разрушен к чертям.
Частенько весь инвариант помещается в #ifdef _DEBUG, чтобы не включаться в финальные версии.
Chief Software Engineer,
Scrum Master, Symbian
Re: Что такое Инвариант?
От: | Chez |
Дата: | 12.05.05 07:12 |
Оценка: |
Здравствуйте, Ignoramus, Вы писали:
I>Честно несколько раз прочитал определение у Страуструпа, но смысл сего понятия от меня ускользает.
I>Пожалуйста объясните, чем являтся инвариант класса с точки зрения практического кода.
I>Что это — функция? Как она используется? И т.п.
MFC's CObject::AssertValid м ему подобные.
Posted via:RSDN@Home;version:1.1.3;muzikstamp:silent