Статьи Королевства Дельфи

       

Часть 4. Некоторые нюансы


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

Добавим еще один интерфейс в модуль CommonInterfaces и назавем его ICallbackInterface2. В интерфейсе опишем процедуру с названием, пересекающимся с ICallbackInterface: ICallbackInterface2 = interface ['{7D501744-B419-11D5-915B-ED714AED3037}'] procedure Callback(Text: String); end; Теперь введем этот интерфейс в главную форму: type TForm1 = class(TForm, ICallBackInterface, ICallbackInterface2) MainMenu: TMainMenu; File1: TMenuItem; … Чтобы компилятор правильно различал вызовы методов Callback от разных интерфейсов, секцию protected перепишем следующим образом: … protected // перенаправляем вызов через ICallBackInterface к процедуре Callback1 procedure ICallBackInterface.Callback = Callback1; // перенаправляем вызов через ICallBackInterface2 к процедуре Callback2 procedure ICallBackInterface2.Callback = Callback2; procedure Callback1(Text: String); procedure Callback2(Text: String); … end; … procedure TForm1.Callback1(Text: String); begin ShowMessage('Из главной формы 1 "' + Text + '"'); end; procedure TForm1.Callback2(Text: String); begin ShowMessage(' Из главной формы 2 "' + Text + '"'); end; И, наконец, в форме TfrmChild нашего пакета строим вызовы этих методов type TfrmChild = class(TForm, IMyInitialize, IMyHello) … aQueryInMainForm: TAction; aQueryInMainForm2: TAction; … procedure aQueryInMainFormExecute(Sender: TObject); procedure aQueryInMainForm2Execute(Sender: TObject); private … procedure TfrmChild.aQueryInMainFormExecute(Sender: TObject); var CallBackInterface: ICallBackInterface; begin if Application.MainForm.GetInterface(ICallBackInterface, CallBackInterface) then CallBackInterface.Callback('Привет от ' + Caption); end; procedure TfrmChild.aQueryInMainForm2Execute(Sender: TObject); var CallBackInterface: ICallBackInterface2; begin if Application.MainForm.GetInterface(ICallBackInterface2, CallBackInterface) then CallBackInterface.Callback('Привет от ' + Caption); end; … Запускаем и убеждаемся в том, что вызываются действительно нужные методы.


Полный исходный код этой части находится в архиве (каталог Step4).

Еще один нюанс, известный программистам COM. Ничто не запрещает вводить в интерфейсы обычные свойства Deplhi (для более простого моделирования). Ограничением, естественно, является только то, что интерфейс не может содержать полей-данных. В интерфейсы должны быть описаны только методы. Вот пример интерфейса содержащего свойства IMainForm = interface ['{765B2E71-B81C-11D5-9160-C43E6EC62937}'] function GetCaption: TCaption; procedure SetCaption(const Value: TCaption); function GetFont: TFont; procedure SetFont(conts Value: TFont); function GetSelf: TForm; property Caption: TCaption read GetCaption write SetCaption; property Font: TFont read GetFont write SetFont; property Self: TForm read GetSelf; end Естественно, при наследовании некоторым классом (скажем, какой-нибудь формой) этого интерфейса необходимо в него ввести реализацию методов GetCaption, SetCaption, GetFont, SetFont, GetSelf .

Ну и напоследок, интерфейсы можно наследовать так же, как и обычные классы. При чем это наследование может быть множественным (как в С++). Пример:
У нас был интерфейс ICallBackInterface: ICallBackInterface = interface ['{7D501743-B419-11D5-915B-ED714AED3037}'] procedure Callback(Text: String); end; Добавляем еще один интерфейс, расширяющий поведение ICallBackInterface ICallBackInterfaceEx = interface(ICallBackInterface) ['{7D501743-B419-11D5-915B-ED714AED3038}'] procedure CallbackEx(Text: String); end; , а в главной форме поменяем наследование TForm1 = class(TForm, ICallBackInterface, ICallbackInterfaceEx) Теперь после компиляции что мы получим?

Вызов Application.MainForm.GetInterface(ICallBackInterface, CallBackInterface) всегда будет возвращать ссылку на кусок виртуальной таблицы методов, содержащей процедуру Callback и только ее (то есть, действительно ссылку ICallBackInterface, хотя мы его явно не наследовали).

А вот вызов Application.MainForm.GetInterface(ICallBackInterfaceEx, CallBackInterface) будет возвращать ссылку на кусок виртуальной таблицы методов, содержащей как процедуру Callback, так и CallbackEx.

Отсюда можно сделать следующие выводы:
  1. Старые приложения (или пакеты - все зависит от места использования интерфейса), не знающего ICallBackInterfaceEx, будут вызывать ICallBackInterface и останутся в работоспособном состоянии.
  2. Новые приложения (или пакеты), уже имеющие сведения о ICallBackInterfaceEx, вполне могут вызывать как ICallBackInterfaceEx, так и ICallBackInterface (в зависимости от прихоти программиста).
То есть, значительно облегчается сопровождения декомпозированного приложения (что так знакомо программистам COM).


Содержание раздела