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

Про Assert vs. Exception

Тут можно МНОГО чего сказать.

Во-первых упомянуть "защитное" и "наступательное" программирование.

"Наступательное" это когда мы ТЕСТИРУЕМ. Тут ВАЖНО, чтобы МАКСИМУМ ошибок "всплывал".

"Защитное" это когда мы отдаём приложение ПОЛЬЗОВАТЕЛЮ, тут важно "ошибки маскировать" и пытаться "восстанавливаться после сбоев".

Это НАДО понимать.

Вообще это СЛОЖНАЯ и ОБШИРНАЯ тема.

Я потом к ней вернусь.

Пока лишь хочу УКАЗАТЬ на ОДНУ РАСПРОСТРАНЁННУЮ ошибку с использованием Assert.

Что-то вроде:

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

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

Понятно, что ПРАВИЛЬНО было написать так:

if Supports(SomeClass, SomeInterface, Instance)) then
 Instance.DoSomeMethod
else
 Assert(false);

Вообще говоря это РАСПРОСТРАНЁННАЯ ошибка "начинающих", но я её в жизни - МНОГО где видел.

В ПЕРВУЮ очередь надо избегать ПОДОБНЫХ Assert. которые смешивают ИНВАРИАНТЫ и логику.

Кстати вот ссылка про использование Assert - http://18delphi.blogspot.ru/2013/04/blog-post.html

1 комментарий:

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

    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;»
    которая и применяется в версии для пользователя.

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