Обработка исключений

Технические приемы, связанные с реализацией интерфейса.

Пример: будет основан на использовании 2х интерфейсов, IWalker и IJumper. Определим их:

Type IWalker = interface

[‘{ - - -}’]

Function Walk:string;

Function Run: string

Procedure SetPos(Value:integer);

Function GetPos:integer;

Property Position: integer read GetPos write SetPos;

End;

IJumper = interface

[‘{ - - -}’]

Function Jump:string;

Function Walk: string

Procedure SetPos(Value:integer);

Function GetPos:integer;

Property Position: integer read GetPos write SetPos;

End;

В примерах, в интерфейсах определяется свойство Position. Свойство интерфейса – это просто имя, отображаемое на методы read и write, нельзя отразить свойство на поле, т.к. интерфейс не может обладать полем.

Приведем пример реализации интерфейса IWalker

TRunner = class (TInterfacedObject, IWalker)

Private

Pos:integer;

Public

Function Walk:string;

Function Run:string;

Procedure SetPos(value:integer);

Function GetPos:integer;

End;

//Один класс реализует один интерфейс напрямую.

Интерфейс IJumper реализуем в 2х разных классах, воспользуемся след. походом: можно делегировать реализацию интерфейса, поддерживаемого некоторым классом объекту, входящему в состав этого класса. При этом доступ к этому объекту осуществляется с использованием свойства. Таким образом код реализации интерфейса будет частью нескольких классов. Для реализации такого подхода используется директива implements.

Пример:

TMyJumper = class (TInterfacedObject, Ijumper)

Private

FJumpImpl: IJumper; //переменная интерфейсного типа

Public

Constructor Create;

Property Jumper: IJumper read FJumpImpl implements IJumper;

End;

// делегирует внутреннему объекту который реализуется через свойство.

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

constructor TMyJumper.Create;

Begin

fJumppImpl:=TjumperImpl.Create{класс в котором будет реализ.внутрненний объект}

End;

Разработаем класс, реализующий интерфейс IJumper.

Type

TjumpImpl = class(TInterfacedObject, IJumper)

Private

Pos:integer;

Public

Function Jump:string;

Function Walk:string;

Procedure SetPos(Value:integer);

Function GetPost:integer;

End;

Если использовать данный код, программа откомпилируется, и заработает без каких - либо видимых проблем. Однако, проверив работу механизма подсчета ссылок, нужно убедиться, что он работает некорректно.

Проблема заключается в том, что когда программа извлекает интерфейс IJumper из объекта TMyJumper, она на самом деле увеличивает и уменьшает счетчик ссылок внутреннего, а не внешнего объекта. Т.е. у нас имеется единый комплексный объект и 2 независимых друг от друга счетчика ссылок. В рез – е объекты данного класса либо слишком долго хранятся в памяти, либо слишком рано уничтожаются. Чтобы решить проблему, необходимо использовать только один счетчик ссылок и перенаправить вызовы _AddRef и _Release {увел или уменьш ссылок} внутреннего объекта внешнему. Существует класс TAgregatedObject, который … Этот класс разработан специально для того, чтобы учитывать ситуацию, когда есть единый класс, состоящий из двух. В результате, в нашей реализации изменится следующее:

Type

TjumpImpl = class(TAgregatedObject, IJumper)

Private

Pos:integer;

Public

Function Jump:string;

Function Walk:string;

Procedure SetPos(Value:integer);

Function GetPost:integer;

End;

Объект, который использует данный класс для того чтобы реализовать интерфейс IJumper {в примере этот объект имеет класс TMyJumper} должен обладать конструктором Create для того, чтобы создать внутренний объект и деструктором для того чтобы его уничтожить. Конструктор внутреннего объекта в качестве параметра должен принимать контейнерный объект, благодаря чему у него появится возможность перенаправлять вызовы IInterface.

Приведем окончательную версию класса TMyJumper:

TMyJumper = class (TInterfacedObject, Ijumper)

Private

Pos:integer;

Public

Constructor Create;

FJumpImpl: TJumperImpl; //переменная интерфейсного типа

Property Jumper: TJumperimpl read FJumpImpl implements IJumper;

Destructor Destroy; override;

End;

Constructor TMyJumper.Create;

Begin

fJumpImpl:=TJumpImpl.Create(self);

End;

Пример #3

Разработаем класс, реализующим оба интерфейса IWalker и IJumper. Один интерфейс реализуем напрямую а другой через делегирование.

TAthlete = class (IInterfacedObject, IWalker, IJumper)

Private

fJumpImpl:=IJumperImpl;

public

constructor Create;

destructor Destroy; override;

function Run:string;virtual;

function IWalker.Walk:=Walk1;

function Walk1: string; virtual;

procedure SetPos(Value:Integer);

function GetPos:integer;

property Jumper: TJumperImpl read fJumpImpl implements IJumper;

End;

Один интерфейс, IWalker реализован напрямую, а реализация интерфейса IJumper делегирована внутреннему объекту fJumpImpl. Реализуя два интерфейса с общим методом(методы имеют одинаковое имя - Walk) можно придти к конфликту имён (особенно если оба интерфейса реализуются напрямую). Решение заключается в переименовании, в данном случае одного из методов при помощи псевдонима (Walk1).

В реализации всех методов класса TAthlete следует обратиться к свойству Position внутреннего объекта fJumpImpl иначе можно получить, что один атлет обладает двумя позициями.

Procedure TAthlete.GetPos;

Begin

Result:=fJumpImpl.Position;

End;

Function TAthlete.Run:string;

Begin

fJumpImpl.Position:= fJumpImpl.Position +2;

Result:= IntToStr(fJumpImpl.Position)+’Бежит’;

End;

TMyJumper.Create; Walk __ __
MyJumper
Athlete
Runner


*По нажатию кнопки:

1) Создается объект соответ.класса. В ListBox записывается строка что он создан. Затем вызываются по очереди методы данного класса, которые являются функциями типа string и их значение записывается в ListBox. Также записываются значения свойства Position, каждый метод увеличивает его на определенное число значений. Плюс описание требуемого перемещения. И обязательно деструктор.

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

Если в процессе выполнения программы что – либо работает неверно, Дельфи генерирует исключительную ситуацию. В независимости от того, где произошла ошибка (в коде программы, внутри компонента, в ОС) обработка возникшей проблемы осуществляется унифицировано. В момент возникновения исключения управление передается из кода, ставшего причиной исключения, коду из которого было выполнено обращение. Если исключения в программе никак не обрабатываются они передаются из подпрограммы или метода в вызывающую подпрограмму или метод и так вплоть до глобального обработчика Aplication. Таким образом среда Дельфи сама может обработать исключение, а затем попытается продолжить выполнение программы.

При обработке исключений используются следующие ключевые слова:

Try – помечает начало защищенного участка кода

Except – бозначает конец защищенного участка и начало операторов обработки исключения

Синтаксис следующий:

On тип исключения do действия

Finally – специфицирует код, который выполняется в любом случае, даже если произошло исключение.

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

Raise – позволяет породить исключительную ситуацию.

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

Например, теоретически можно написать следующий код:

Function Ex1(A,B:integer):integer;

Begin

Try

Result:= A div B; //возможно деление на 0

……………….

Result:=Result+1;

Except

On EDivByZero do

Result:=0;

End;

End;

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

Использование блока finally.

Пример:

Будут выполняться длительные вычисления а на экране будет отображаться курсов в виде песочных часов.

Function Tform1.Btn1Click(sender:object);

Var I,j:integer;

Begin

Screen.Cursos:=czHourGlass;

J:=0;

For i:=1000 downto 0 do

J:= j + j div I; {деление на 0}

messageDlg(‘Total: ’,+IntToStr(j), mtInformation, [mbOK], 0);

screen.Cursos:=csDefault;

End;

При попытке деления на 0 программа прервется, но курсор по умолчанию не будет восстановлен. Для того чтобы исправить ситуацию используется блок try.. finally.

Function Tform1.Btn2Click(sender:object);

Var I,j:integer;

Begin

Screen.Cursos:=czHourGlass;


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: