Создание и использование интерфейсаDelphi , Технологии , OLEСоздание и использование интерфейсаИнтерфейсы играют главную роль в технологиях СОМ (Component Object Model - компонентная модель объектов), CORBA (Common Object Request Broker Architecture - архитектура с брокером требуемых общих объектов) и связанных с ними технологиях удаленного доступа, т. е. технологиях доступа к объектам, расположенным (и выполняющимся) на другой машине. Их основная задача - описать свойства, методы и события удаленного объекта в терминах машины клиента, т. е. на используемом при разработке клиентского приложения языке программирования. С помощью интерфейсов программа клиента обращается к удаленному объекту так, как если бы он был ее собственным объектом. Тема интерфейсов достаточно обширна и интересна. В этой главе даются лишь самые общие сведения об интерфейсах. Сведение этой темы в одну главу с классами не случайно, т. к. интерфейс представляет собой пустой класс, т. е. класс, в котором провозглашены, но никак не расшифрованы свойства и методы. Интерфейсы представляют собой частный случай описания типов. Они объявляются с помощью зарезервированного слова interface. Например: type IEdit = interface procedure Copy; stdcall; procedure Cut; stdcall; procedure Paste; stdcall; function Undo: Boolean; stdcall; end; Такое объявление эквивалентно описанию абстрактного класса в том смысле, что провозглашение интерфейса не требует расшифровки объявленных в нем свойств и методов. В отличие от классов интерфейс не может содержать поля, и, следовательно, объявляемые в нем свойства в разделах read и write могут ссылаться только на методы. Все объявляемые в интерфейсе члены размещаются в единственной секции public. Методы не могут быть абстрактными (abstract), виртуальными (virtual), динамическими (dynamic) или перекрываемыми (override). Интерфейсы не могут иметь конструкторов или деструкторов, т. к. описываемые в них методы реализуются только в рамках поддерживающих их классов, которые называются интерфейсными. Если какой-либо класс поддерживает интерфейс (т. е. является интерфейсным), имя этого интерфейса указывается при объявлении класса в списке его родителей: TEditor = class(TInterfacedObject, IEdit) procedure Copy; stdcall; procedure Cut; stdcall; procedure Paste; stdcall; function Undo: Boolean; stdcall; end; В отличие от обычного класса интерфейсный класс может иметь более одного родительского интерфейса: type IMylnterface = interface procedure Delete; stdcall; end; TMyEditor = class(TInterfacedObiect, lEdit, IMylnterface) procedure Copy; stdcall; procedure Cut; stdcall; procedure Paste; stdcall; function Undo: , Boolean; stdcall; procedure Delete; stdcall; end; В любом случае в разделе реализации интерфейсного класса необходимо описать соответствующие интерфейсные методы. Если, например, объявлен интерфейс IPaint = interface procedure CirclePaint(Canva: TCanvas; X, Y, R: Integer); procedure RectPaint(Canva: TCanvas; X1, Y1, X2, Y2: Integer); end; и использующий его интерфейсный класс TPainter = class(TInterfacedObject, IPaint) procedure CirclePaint(Canva: TCanvas; X, Y, R: Integers); procedure RectPaint(Canva: TCanvas; X1, Y1, X2, Y2: Integer); end; то в разделе implementation следует указать реализацию методов: procedure TPainter.CirclePaint(Canva: TCanvas; X, Y, R: Integers; begin with Canva do Ellipse(X, Y, X + 2 * R, Y + 2 * R); end; procedure TPainter.RectPaint(Canva: TCanvas; X1, Y1, X2, Y2: Integer); begin with Canva do Rectangle(XI, Yl, X2, Y2) end; Теперь можно объявить интерфейсный, объект класса TPainter, чтобы с его помощью нарисовать окружность и квадрат: procedure TForm1.PaintBoxIPaint(Sender: TObject); var Painter: IPaint; begin Painter := TPainter.Create; Painter.CirclePaint(PaintBoxl.Canvas, 10, 0, 10); Painter.RectPaint(PaintBoxl.Canvas, 40, 0, 60, 20); end; Несмотря на то что интерфейс всегда объявляется до объявления использующего его интерфейсного класса и, следовательно, известен компилятору, его методы обязательно должны быть перечислены в объявлении класса. В нашем случае простое указание type TPainter = class(TInterfacedObject, IPaint) end; было бы ошибкой: компилятор потребовал бы вставить описание методов CirclePaint и RectPaint. Подобно тому как все классы в Object Pascal порождены от единственного родителя TObject, все интерфейсные классы порождены от общего предка TInterfacedObject. Этот предок умеет распределять память для интерфейсных объектов и использует глобальный интерфейс lunknow: type TInterfacedObject = class(TObject, lUnknown)private FRefCount: Integer; protected function Querylnterface( const IID: TGUID; out Obj): Integer; stdcall; function _AddRef: Integer; stdcall; function _Release: Integer; stdcall; public property RefCount: Integer read FRefCount; end; Если бы в предыдущем примере класс TPainter был описан так: TPainter = class(IPaint) procedure CirclePaint(Canva: TCanvas; X, Y, R: Integer); procedure RectPaint(Canva: TCanvas; X1, Y1, X2, Y2: Integer); end; компилятор потребовал бы описать недостающие методы Queryinterface, _Add И _Release класса TInterfacedObject. Поле FRef Count этого класса служит счетчиком вызовов интерфейсного объекта и используется по принятой в Windows схеме: при каждом обращении к методу Add интерфейса IUnknow счетчик наращивается на единицу, при каждом обращении к Release - на единицу сбрасывается. Когда значение этого поля становится равно 0, интерфейсный объект уничтожается и освобождается занимаемая им память. Если интерфейс предполагается использовать в технологиях COM/DCOM или CORBA, его методы должны описывать с директивой stdcall или (для объектов Автоматизации) safecall К интерфейсному объекту можно применить оператор приведения типов as, чтобы использовать нужный интерфейс: procedure PaintObjects(P: TInterfacedObject) var X: IPaint; begin try X := P as IPaint; X.CirclePaint(PaintBoxl.Canvas, 0, 0, 20) except ShowMessage('Объект не поддерживает интерфейс IPaint') end end; Встретив такое присваивание, компилятор создаст код, с помощью которого вызывается метод Queryinterface интерфейса IUnknow с требованием вернуть ссылку на интерфейс IPaint. Если объект не поддерживает указанный интерфейс, возникает исключительная ситуация. Интерфейсы, рассчитанные на использование в удаленных объектах, должны снабжаться глобально-уникальным идентификатором (guiD). Например: IPaint = interface ['{A4AFEB60-7705-11D2-8B41-444553540000}'] procedure CirclePaint(Canva: TCanvas; X, Y, R: Integer); procedure RectPaint(Canva: TCanvas; Xl, Yl, X2, Y2: Integer); end; Глобально-уникальные идентификаторы создаются по специальной технологии, гарантирующей ничтожно малую вероятность того, что два guid совпадут. Эта технология включена в Windows 32: чтобы получить guid для вновь созданного интерфейса в среде Delphi, достаточно нажать клавиши Ctrl+Shift+G. Для работы с guid в модуле System объявлены следующие типы: type PGUID = ^TGUID; TGUID = record Dl: LongWord; D2: Word; D3: Word; D4: array[0..7] of Byte; end; Программист может объявлять типизированные константы типа tguid, например: const IID_IPaint: TGUID= ['{A4AFEB61-7705-11D2-8B41-444553540000}'] ; Константы guid могут использоваться вместо имен интерфейсов при вызове подпрограмм. Например, два следующих обращения идентичны: procedure Paint(const IID: TGUID); Paint(IPaint) ; Paint(IID_Paint); С помощью зарезервированного слова implements программист может делегировать какому-либо свойству некоторого класса полномочия интерфейса. Это свойство должно иметь тип интерфейса или класса. Если свойство имеет тип интерфейса, имя этого интерфейса должно указываться в списке родителей класса, как если бы это был интерфейсный класс: type IMylnterface = interface procedure P1; procedure P2; end; TMyClass = class(TObject, IMylnterface) FMyInterface: IMylnterface; property Mylnterface: IMylnterface read FMyInterface implements IMylnterface; end; Обратите внимание: в этом примере класс TMyciass не является интерфейсным, т. е. классом, в котором исполняются методы p1 и P2. Однако если из него убрать определение уполномоченного свойства Mylnterface, он станет интерфейсным, и в нем должны быть описаны методы интерфейса IMylnterface. Уполномоченное свойство обязательно должно иметь часть read. Если оно имеет тип класса, класс, в котором оно объявлено, не может иметь других уполномоченных свойств. Создание и использование интерфейса - это важная роль в технологиях СОМ, CORBA и связанных с ними технологиях удаленного доступа, позволяющая описать свойства, методы и события удаленного объекта в терминах машины клиента. Комментарии и вопросыПолучайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.
|
||||
©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007 |