Технические приемы, связанные с реализацией интерфейса.
Пример: будет основан на использовании 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, каждый метод увеличивает его на определенное число значений. Плюс описание требуемого перемещения. И обязательно деструктор.
Дата добавления: 2016-07-27; просмотров: 1332;