Проектирование по контракту и утверждение заявлений

Я заинтересован в Design by Contract подход. Кажется, что для preconditions проверенные исключения должны быть использованы для их применения.
Но для post-conditions а также class-invariants я думаю что assertions являются предпочтительными.
Я прав? Если я прав, зачем post-conditions а также class-invariants утверждения, которые могут быть отключены, разрешены? Разве постусловия и инварианты также не должны соблюдаться?

3 ответа

Решение

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

С другой стороны, предварительные условия могут потерпеть неудачу, если пользователи этого компонента неверны. Тесты на самом компоненте не могут их проверить, поэтому необходимо активнее проваливаться, чтобы эти модульные тесты не прошли.

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

Вы не должны относиться к ним по-разному - все они должны быть утверждениями.

ОП говорит:

... Похоже, что для предварительных условий должны применяться проверенные исключения. ... почему для постусловий и инвариантов классов допускаются утверждения, которые могут быть отключены? Разве постусловия и инварианты также не должны соблюдаться?

Похоже, вы предлагаете, чтобы предварительные условия, постусловия и инварианты классов были всегда включены и всегда проверялись службой (метод / вызываемый объект). Если мы говорим о Design-by-Contract (DBC), который был создан Бертраном Мейером, то это не так. Мейерс утверждает, что с точки зрения производственного кода эти условия должны быть обеспечены только в одном месте, либо клиентом (вызывающей стороной), либо службой (вызываемой стороной) - это контракт между клиентом и службой. Напротив, защитное программирование говорит, что вы должны кодировать проверку в обоих местах (что Майерс считает расточительным и добавляет ненужную сложность).

Контрактная часть DBC заключается в том, что в спецификации будет понятно, кто за что отвечает: если клиент выполнит предварительные условия (и служба может предположить, что они будут выполнены), то служба обеспечит постусловия (и звонящий может предположить, что они будут правдой). Мейерс определенно понимал, что для службы целесообразно проверять предварительные условия / постусловия / инварианты для целей тестирования и отладки (чтобы гарантировать, что система будет быстро отказывать во время тестирования / отладки), поэтому целесообразно утверждать эти условия и включите утверждения во время тестирования / отладки, но предполагается, что эти проверки можно отключить или удалить для производства.

Например, если вы разрабатываете стек таким образом, чтобы вызывающий отвечал за проверку того, что стек не пуст перед вызовом pop() (предварительное условие), то вам также не следует кодировать метод pop () как часть производственного кода. проверка того, что стек не пустой, а также наличие проверяемого исключения как части сигнатуры метода для обработки условия. Можно утверждать предварительное условие, чтобы помочь с проверкой и отладкой, но цель состоит в том, что после завершения разработки и тестирования (код, по-видимому, не содержит ошибок), рабочий код будет работать только с вызывающей стороной, обеспечивающей предварительное условие, а не вызываемый (если именно так вы разработали контракт для API).

Если у вас есть проверенное исключение как часть метода pop (), и вы проверяете, является ли стек пустым в методе pop () как часть постоянно включенного, должен ли обрабатываться производственный код, то вы говорите, что служба (вызываемый абонент) берет на себя ответственность за проверку и обещает выдать исключение, если стек пуст - теперь это часть постусловия. В этом случае вызывающему не нужно явно проверять это - они должны просто обработать исключение. В противном случае, если и вызывающая сторона, и вызываемая сторона проверяют, является ли стек первым пустым, то это не проектирование по контракту.

В заключение отметим, что некоторые люди считают, что это означает, что Мейер говорит, что вызывающий всегда должен отвечать за проверку не пустого стека или что параметры не равны нулю и т. Д. Не так - Мейерс говорит только, что вы должны четко спецификации о предварительных условиях и пост-условиях, чтобы клиенты и услуги могли быть правильно закодированы. Вы как разработчик и разработчик API решаете, что входит в предварительные условия и постусловия. Если вы можете быть уверены, что клиенты (вызывающие) будут обеспечивать предварительные условия (например, потому что это внутренний класс, и вы контролируете везде, где вызывается служба / метод), то сделайте "стек не пуст" или " Параметр a-is-not-null"является частью предварительного условия и возлагает ответственность на клиента. Однако, если вы не считаете, что это разумное предположение (например, его общедоступный API или клиенты не могут быть проверены или протестированы для обеспечения соответствия), и последствия невыполненных предварительных условий являются серьезными, то не делайте эти предположения частью предварительное условие: сделайте проверку в сервисе и сделайте проверенное исключение частью подписи - сделайте это частью пост-условия, что если стек вызывается при вызове pop (), то служба выбросит проверенный исключение.

[[Извините за напыщенный ответ - я большой поклонник Бертрана Мейера и Design-by-Contract.]]

Другие вопросы по тегам