пятница, 14 февраля 2014 г.

Про Assert и Contract

http://programmingmindstream.blogspot.ru/2014/02/assert-vs-exception.html?showComment=1392358212790#c3301455318549552072

Цитирую:


«Побоюсь показаться БАНАЛЬНЫМ, но я много видел в коде примерно следующее: 

Assert(Supports(SomeClass, SomeInterface, Instance));
Instance.DoSomeMethod; 

Понятно, что ПРАВИЛЬНО было написать так: 
if Supports(SomeClass, SomeInterface, Instance)) then
  Instance.DoSomeMethod 
else
  Assert( false );»
-- Не понятно - поясните.
Первый вариант нахожу всем лучше второго, за исключением особенности Assert, состоящей в том, что при сборке версии для пользователя (без отладки) Assert-ы удаляются компилятором.
Ввиду этой особенности, Assert не используется вообще, вместо него применяется своя процедура Contract:
«resourcestring
SEContractViolation = 'Произошла внутренняя ошибка программы. Обратитесь к разработчику.';
SEViolationDetails = 'Служебная информация: модуль %s, строка %d';

procedure Contract(ACondition: Boolean; const AMessage: String = '');
var
ExceptAddr: Pointer;
location: TLocation;
message: String;
begin
if ACondition then Exit;
// получаем адрес места, откуда вызвали Contract
asm
mov EAX, [EBP+$04]
mov ExceptAddr, EAX
end;
if Assigned(GetLocationProc) then
location := GetLocationProc(ExceptAddr);
if AMessage = '' then
message := SEContractViolation
else
message := AMessage;
raise ESContract.CreateFmt(message + ^M^J + SEViolationDetails, [location.SourceName, location.LineNumber]) at ExceptAddr
end;»
которая и применяется в версии для пользователя.

Конец цитаты.

Скажу коротко.

Assert и Contract это РАЗНЫЕ инструменты. Два разных инструмента.

Assert это ИНВАРИАНТ, это то, что у пользователя НЕ СЛУЧАЕТСЯ в принципе.

Контракт это НЕ ИНВАРИАНТ, а защита, от того, что "а вдруг чего случится" или "а вдруг недодумали".

И я ПРАКТИЧЕСКИ УВЕРЕН, что NameRec это понимает.

И ОСОЗНАННО применяет только ОДИН из них.

Ну что же. Каждый имеет право на выбор.

И выбор - осознанный.

Я же применяю ОБЕ техники. Мне лично - это ближе.

Никому ничего не навязываю.

P.S. Да! И СПАСИБО NameRec'у за ХОРОШИЙ вопрос. Тему на самом деле можно развивать и дальше.

3 комментария:

  1. Действительно, есть достаточно большой положительный опыт применения именно контрактов, которые контролируются при выполнении приложения у пользователя. Протестировать всё на 100% нереально, но нарушение контракта приводит к исключениям, попадающим в лог вместе со стеком вызовов, что часто помогает выявлению причин и исправлению проблемы.
    Я тоже никому ничего не пытаюсь навязать, но с давних пор меня смущала ограниченность Assert.
    Им можно воспользоваться только при "белом тестировании", которое может выполнять только программист, на настоящее тестирование (по крайней мере у нас) такая версия никогда не передаётся, поскольку тестированию должно подвергаться приложение, которое уйдёт пользователю, а не какая-то специальная версия с отладочной информацией.
    "Белое" же тестирование у нас практически не востребовано. Понятно, если ищем ошибку - включается всё, и отладка и FullDebugMode у FastMM... Тут могли бы работать и Assert, но толку от них... Они будут только уводить от проблемы, которая возникает на реальной версии (без отладки), и вообще, какой смысл искать ошибки в версии программы, отличной от того, что будет передано пользователю...
    А раз так, то Assert для нас совершенно бесполезен.
    Ну, разве что, при автоматизированном тестировании. Но в тестах всё равно, что использовать: Assert или Contract.
    Contract для нас привычнее и больше соответствует принципу наименьшего удивления. Кроме того, а если код из тестов мигрирует в приложение? Там-то Assert нежелателен...
    Ну и, разумеется, было бы интересно узнать, как и где применяют Assert люди, находящие его полезным...

    ОтветитьУдалить
    Ответы
    1. Идея понятна. Я сам этим пользуюсь. Assert же я применяю, как вы правильно заметили в "белом тестировании".

      Удалить
  2. Вдогонку... Мы могли бы продолжить "спорить", но разве что только для "углубления темы".

    ОтветитьУдалить