Delphi и COM
f65d50f6

Использование интерфейсов внутри программы


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

В качестве примера рассмотрим MDI-приложение, имеющее много различных форм и единую панель инструментов. Предположим, что на этой панели инструментов имеются команды «Сохранить», «Загрузить» и «Очистить», однако каждое из окон реагирует на эти команды по-разному.

Создадим модуль с объявлениями интерфейсов:

unit ToolbarInterface; interface type   TCommandType = (ctSave, ctLoad, ctClear);   TCommandTypes = set of TCommandType;   TSaveType = (stSave, stSaveAS);   IToolBarCommands = interface   ['{B5266AE1-5E77-11D4-84DD-9153115ABFC3}']     function SupportedCommands: TCommandTypes;     function Save(AType: TSaveType): Boolean;     procedure Load;     procedure Clear;   end; implementation end.

Интерфейс IToolBarCommands описывает набор методов, которые должны реализовать формы, поддерживающие работу с панелью кнопок. Метод SupportedCommands возвращает список поддерживаемых формой команд.


Создадим три дочерние формы — Form2, Form3 и Form4 — и установим им свойство FormStyle = fsMDIChild.

Form2 умеет выполнять все три команды:

type   TForm2 = class(TForm, IToolBarCommands)   private     function SupportedCommands: TCommandTypes;     function Save(AType: TSaveType): Boolean;     procedure Load;     procedure Clear;   end; { TForm2 }   procedure TForm2.Clear; begin   ShowMessage('TForm2.Clear'); end;   procedure TForm2.Load; begin   ShowMessage('TForm2.Load'); end;   function TForm2.Save(AType: TSaveType): Boolean; begin   ShowMessage('TForm2.Save');   Result := TRUE; end;   function TForm2.SupportedCommands: TCommandTypes; begin   Result := [ctSave, ctLoad, ctClear] end;

Form3 умеет выполнять только команду Clear:

type   TForm3 = class(TForm, IToolBarCommands)   private     function SupportedCommands: TCommandTypes;     function Save(AType: TSaveType): Boolean;     procedure Load;     procedure Clear;   end; { TForm3 }   procedure TForm3.Clear; begin   ShowMessage('TForm3.Clear'); end;   procedure TForm3.Load; begin   // Метод ничего не делает, но должен присутствовать   // для корректной реализации интерфейса end;   function TForm3.Save(AType: TSaveType): Boolean; begin end;   function TForm3.SupportedCommands: TCommandTypes; begin   Result := [ctClear] end;

И наконец, Form4 вообще не реализует интерфейс IToolBarCommands и не откликается ни на одну команду.

На главной форме приложения поместим ActionList и создадим три компонента TAction. Кроме того, разместим на ней TToolBar и назначим ее кнопкам соответствующие TAction.

type   TForm1 = class(TForm)     ToolBar1: TToolBar;     ImageList1: TImageList;     ActionList1: TActionList;     acLoad: TAction;     acSave: TAction;     acClear: TAction;     tbSave: TToolButton;     tbLoad: TToolButton;     tbClear: TToolButton;     procedure acLoadExecute(Sender: TObject);     procedure ActionList1Update(Action: TBasicAction;       var Handled: Boolean);     procedure acSaveExecute(Sender: TObject);     procedure acClearExecute(Sender: TObject);   end;

Наиболее интересен метод ActionList1Update, в котором проверяются поддерживаемые активной формой команды и настраивается интерфейс главной формы. Если нет активной дочерней формы либо она не поддерживает интерфейс IToolBarCommands, все команды запрещаются, в противном случае — разрешаются только поддерживаемые формой команды.

procedure TForm1.ActionList1Update(Action: TBasicAction;   var Handled: Boolean); var   Supported: TCommandTypes;   TC: IToolBarCommands; begin   if Assigned(ActiveMDIChild)      and ActiveMDIChild.GetInterface(IToolBarCommands, TC) then     Supported := TC.SupportedCommands   else     Supported := [];   acSave.Enabled := ctSave in Supported;   acLoad.Enabled := ctLoad in Supported;   acClear.Enabled := ctClear in Supported; end;

При активизации команд проверяется наличие активной дочерней формы, у нее запрашивается интерфейс IToolBarCommands и вызывается соответствующий ему метод:

procedure TForm1.acLoadExecute(Sender: TObject); var   TC: IToolBarCommands; begin   if Assigned(ActiveMDIChild)      and ActiveMDIChild.GetInterface(IToolBarCommands, TC) then     TC.Load; end;   procedure TForm1.acSaveExecute(Sender: TObject); var   TC: IToolBarCommands; begin   if Assigned(ActiveMDIChild)      and ActiveMDIChild.GetInterface(IToolBarCommands, TC) then     if not TC.Save(stSaveAS) then       ShowMessage(‘Not Saved !!!’); end;   procedure TForm1.acClearExecute(Sender: TObject); var   TC: IToolBarCommands; begin   if Assigned(ActiveMDIChild)      and ActiveMDIChild.GetInterface(IToolBarCommands, TC) then     TC.Clear; end;

Работа программы представлена на рисунке.

Того же эффекта можно добиться и другими методами (например, унаследовав все дочерние формы от единого предка либо обмениваясь с ними сообщениями), однако эти методы имеют ряд существенных недостатков.

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



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