Комплексный контроль за качеством кода
Страница 5. Тотальный контроль


5. Тотальный контроль

В первой и второй главе обсуждалась важность применения в программах процедуры Assert с уникальными метками для быстрой локализации и идентификации ошибок. В предыдущей главе обсуждался принцип разделения всех нештатных ситуаций на две группы, требующих различного подхода. Следование этим принципам поможет вам улучшить качество кода при написании больших проектов.

Однако существует еще одна проблема, связанная с тем, что широко используемая библиотека VCL, а также множество других естественно не используют выше указанные принципы. Например, при выполнении следующего кода возникнет исключение от VCL с сообщением "List index out of bounds", так как мы запрашиваем на один элемент больше, чем есть в списке. Получив сообщение о такой ошибке от пользователя, вы вряд ли сможете определить место программы, где эта ошибка произошла. Поиск такой ошибки затруднен, даже если она возникла на вашем компьютере, на том, на котором вы разрабатываете программу. А если такая ошибка возникла у далекого пользователя, сложность возрастает во много раз, так как вам предстоит еще и "выбить" всю информацию об ошибке у человека далекого от Delphi в частности, и от программирования вообще. 

for i:=0 to FList.Count do
FObj := TObject(FList[i]);

Та же самая ситуация произойдет, если вы попытаетесь обратиться по "бешеному" указателю - указателю в котором хранится постороннее значение указывающее в никуда. Вы получите малоинформативное сообщение, которое никак не указывает на место, где эта ошибка произошла. Используя вышеописанные принципы, вы сможете перехватить эту ошибку, распознать ее как фатальную и закрыть программу. Однако вы не сможете выдать информативное сообщение о месте где эта ошибка произошла. И ее локализация и исправление будут сильно затруднены.

Одним из успешных вариантов решения такой проблемы является помещение каждой (каждой !) процедуры в конструкцию try-except-end. При этом локальный обработчик исключения ловит возникшую ошибку и заменяет ее своей с указанием уникального идентификатора. При этом предыдущий пример может выглядеть так. 

try
for i:=0 to FList.Count do FObj := TObject(FList[i]);
except
raise AssertInternal('{88A4613E-0ACB-11D4-ACCF-009027350D25}');
end;

Процедура AssertInternal "наследует" сообщение от возникшего исключения, плюс, добавляет к нему свой уникальный идентификатор. При возникновении ошибки, пользователю будет выдано соответствующее предупреждение, после чего программа закроется. Возможна запись предупреждающего сообщения в специальный файл, который впоследствии может быть выслан вам пользователем.

Получив информацию о возникшей ошибке вы можете:

  • с точностью до процедуры узнать место возникновения ошибки;
  • узнать тип исключения и параметры ошибки (например ошибочный индекс или адрес по которому произошел ошибочный доступ к памяти).

Такая информация значительно облегчает поиск и исправление ошибок в очень большом проекте.

6. Заключение

Все вышеперечисленное являлось лишь приблизительным рецептом и пищей для размышления. Точный вариант комплексной системы обработки ошибок приведен в прилагаемом архиве. Модуль ATSAssert.pas содержит примеры процедур и функций для обработки фатальных ошибок. Модуль ATSDialogMain.pas содержит примеры их использования.

 

 
« Предыдущая статья   Следующая статья »