Delphi的基于接口(IInterface)的监听器模式(观察者模式 )

2016-11-21 08:29:13来源:cnblogs.com作者:子航人点击

本文来自:http://www.cnblogs.com/hezihang/p/6083555.html

Delphi采用接口方式设计模块,可以降低模块之间的耦合,便于扩展和维护。本文提供一个实现基于接口(IInterface)方式的监听器模式(观察者模式、订阅者模式),实现一个自动多播器。

下面程序在Berlin下测试通过,其他Delphi版本未测试,未进行跨平台测试(应该可以支持)

1.prepare

在观察者模式中采用接口,可以将相关函数汇合为接口。

举例:假设我们窗口有一个TTreeView,用于显示应用中的对象,用户通过点击TreeView中的不同对象,切换其他多个不同窗口中的显示内容。

在传统的方式下,维护一个通知列表,可以采用TreeView.OnChange事件中调用所有通知,然后处理如下:(也可采用多播方式,详见:http://www.cnblogs.com/hezihang/p/3299481.html)

procedure TForm2.TreeView1Change(Sender: TObject; Node: TTreeNode);var  L:TTVChangedEvent;begin  for L in FList do  //FList:TList<TTVChangedEvent>    L(Sender, Node);end;

显然采用传统方式,各窗口都需要uses TreeView所在窗口。

另外,如果TreeView所在窗口还有其他事件需要对多个外部窗口或对象进行通知,

则再需要建立一个通知列表。

采用事件方式,需要自己维护一个或多个通知列表,同时各个使用事件的单元都需要引用事件源的单元。

 2.

如果我们采用接口方式,将所有事件包含进去,由TreeView所在窗口去调用:

type  {$M+}  ICurrentStateObserver=interface     ['{20E8D6CB-3BCF-4DAE-A6CE-FEA727133C57}']     procedure OnCurrentObjectChange(CurObj:Pointer);     procedure OnDataReceive(Buf:Pointer; Size:Integre);     procedure OnResize(W, H:Integer);  end;  {$M-}

注意实现自动观察者的接口必须打开RTTI,{$M+}

然后,只需要如下调用,就可以让所有监听者(观察者)的OnResize被调用(接口内所有方法均可被调用):

procedure TForm2.FormResize(Sender: TObject);begin  CurrentStateDispatcher.Source.OnResize(Width, Height);end;

其中:

(1).

CurrentStateDispatcher.Source:ICurrentStateObserver
是一个虚拟接口,也是ICurrentStateObserver类型。调用此接口内的方法,就自动调用所有观察者所对应方法。
这样我们只需要调用
CurrentStateDispatcher.Source.OnCurrentObjectChange(...);CurrentStateDispatcher.Source.OnDataReceive(...);CurrentStateDispatcher.Source.OnResize(...);
就可以实现所有观察者的调用。

(2).
CurrentStateDispatcher:IInterfaceObservable<ICurrentStateObserver>
IInterfaceObservable<ICurrentStateObserver>是一个实现ICurrentStateObserver的多播监听器模式(观察者模式)的接口:
  IInterfaceObservable < T: IInterface >= interface    procedure AddObserver(const aListener: T);    procedure RemoveObserver(const aListener: T);    function GetSource: T;    property Source: T read GetSource;  end;

AddObserver是添加监听者,RemoveObject是删除观察者

Source就是前面提到的用于多播调用的虚拟接口。

(3).在使用模式的对象中声明:
  TForm2=class(TForm)  ....  private     FCurrentStateDispatcher:IInterfaceObservable<ICurrentStateObserver>;  public    property CurrentStateDispatcher:IInterfaceObservable<ICurrentStateObserver> read FCurrentStateDispatcher;  end;
3.
下面我们看一个完整的使用例子:
uMainForm.pas
unit uMainForm;interfaceuses  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,  Vcl.Controls, Vcl.Forms, Vcl.Dialogs , uInterfaceObservable, Vcl.StdCtrls,  Vcl.ComCtrls;type  {$M+}  ITestObserver=interface  ['{FE7F7C11-13BC-472A-BB7A-6536E20BCEDD}']    procedure OnClick(Sender:TObject);    procedure OnResize(Sender:TObject; X, Y:Integer);  end;  {$M-}  TForm2=class;  TObserver1=class(TInterfacedObject, ITestObserver)    F:TForm2;    procedure OnClick(Sender:TObject);    procedure OnResize(Sender:TObject; W, H:Integer);    constructor Create(Owner:TForm2);  end;  TObserver2=class(TInterfacedObject, ITestObserver)    F:TForm2;    procedure OnClick(Sender:TObject);    procedure OnResize(Sender:TObject; W, H:Integer);    constructor Create(Owner:TForm2);  end;  TForm2 = class(TForm)    Memo2: TMemo;    procedure FormClick(Sender: TObject);    procedure FormResize(Sender: TObject);    procedure FormCreate(Sender: TObject);    procedure FormDestroy(Sender: TObject);private    { Private declarations }    FTestDispatcher:IInterfaceObservable<ITestObserver>;  public    { Public declarations }    property TestDispatcher:IInterfaceObservable<ITestObserver> read FTestDispatcher;  end;var  Form2: TForm2;implementation{$R *.dfm}procedure TForm2.FormClick(Sender: TObject);begin  FTestDispatcher.Source.OnClick(Sender);end;procedure TForm2.FormCreate(Sender: TObject);begin    FTestDispatcher:=TDioInterfaceDispatcher<ITestObserver>.Create;    FTestDispatcher.AddObserver(TObserver1.Create(Self));    FTestDispatcher.AddObserver(TObserver2.Create(Self));end;procedure TForm2.FormDestroy(Sender: TObject);begin   FTestDispatcher:=nil;end;procedure TForm2.FormResize(Sender: TObject);var//  i:Integer;//  T:LongWord;  W, H:Integer;begin  W:=Width;  H:=Height;//  T:=GetTickCount;//  for i := 0 to 1000000 do  TestDispatcher.Source.OnResize(Sender, W, H);//  ShowMessage(IntToStr(GetTickCount- T));end;{ TObserver1 }constructor TObserver1.Create(Owner: TForm2);begin  F:=Owner;end;procedure TObserver1.OnClick(Sender: TObject);begin  F.Memo2.Lines.Add('TObserver1.OnClick');end;procedure TObserver1.OnResize(Sender: TObject; W, H:Integer);begin  F.Memo2.Lines.Add(Format('TObserver1.OnResize:%d, %d', [W, H]));end;{ TObserver2 }constructor TObserver2.Create(Owner: TForm2);begin  F:=Owner;end;procedure TObserver2.OnClick(Sender: TObject);begin  F.Memo2.Lines.Add('TObserver2.OnClick');end;procedure TObserver2.OnResize(Sender: TObject; W, H:Integer);begin  F.Memo2.Lines.Add(Format('TObserver2.OnResize:%d, %d', [W, H]));end;end.

uMainForm.dfm

object Form2: TForm2  Left = 0  Top = 0  Caption = 'Form2'  ClientHeight = 309  ClientWidth = 643  OnClick = FormClick  OnCreate = FormCreate  OnDestroy = FormDestroy  OnResize = FormResize  TextHeight = 13  object Memo2: TMemo    Left = 0    Top = 152    Width = 643    Height = 157    Align = alBottom    TabOrder = 0  endend

 4.

 下面是uInterfaceObservable.pas

unit uInterfaceObservable;interfaceuses System.Generics.Collections, System.TypInfo, System.Rtti;type  IInterfaceObservable < T: IInterface >= interface    procedure AddObserver(const aListener: T);    procedure RemoveObserver(const aListener: T);    function GetSource: T;    property Source: T read GetSource;  end;  TDioInterfaceDispatcher<T: IInterface> = class(TInterfacedObject, IInterfaceObservable<T>)  protected    class var FTypeInfo: PTypeInfo;    class var FMethods: TArray<TRttiMethod>;    class var FIID: TGUID;    class constructor Create;  protected    FList: TList<T>;    FVirtualSource, FSource: T;    FVirtualInterface: TVirtualInterface;    FEvents: TObjectList<TList<TMethod>>;    procedure MethodInvoke(Method: TRttiMethod; const Args: TArray<TValue>;      out Result: TValue);  public    procedure AddObserver(const aListener: T);    procedure RemoveObserver(const aListener: T);    function GetSource: T;    constructor Create;    destructor Destroy; override;    property Source: T read FSource;  end;implementationuses System.SysUtils;{ TDioDispatcher<T> }procedure TDioInterfaceDispatcher<T>.AddObserver(const aListener: T);type  TVtable = array [0 .. 3] of Pointer;  PVtable = ^TVtable;  PPVtable = ^PVtable;var  i: Integer;  M: TMethod;  P: Pointer;begin  FList.Add(aListener);  P:=IInterface(aListener);//  P := IInterfaceGetObject(aListener).GetObject;  for i := 0 to FEvents.Count - 1 do  begin    // 3 is offset of Invoke, after QI, AddRef, Release    M.Code := PPVtable(P)^^[3 + i ] ;    M.Data := P;    FEvents[i].Add(M);  end;  if FList.Count=1 then    FSource:=aListener  else    FSource:=FVirtualSource;end;procedure TDioInterfaceDispatcher<T>.MethodInvoke(Method: TRttiMethod;  const Args: TArray<TValue>; out Result: TValue);var  L:TList<TMethod>;  M:TMethod;  i:Integer;begin  L:=FEvents[Method.VirtualIndex-3];  i:=0;  while i<L.Count do  begin    M:=L[i];    Args[0]:=M.Data;    System.Rtti.Invoke(M.Code, Args, Method.CallingConvention, nil);    if (M=L[i]) then      Inc(i);  end;end;constructor TDioInterfaceDispatcher<T>.Create;var  i: Integer;  LMethod: TRttiMethod;  E: TList<TMethod>;  S:String;begin  inherited Create;  FEvents := TObjectList<TList<TMethod>>.Create(True);  FList := TList<T>.Create;  FVirtualInterface := TVirtualInterface.Create(FTypeInfo);  FVirtualInterface.OnInvoke := Self.MethodInvoke;  FVirtualInterface.QueryInterface(FIID, FVirtualSource);  Assert(Assigned(FVirtualSource), '未找到接口' + GUIDToString(FIID));  FSource:=FVirtualSource;  for i := 0 to High(FMethods) do  begin    E := TList<TMethod>.Create;//TEvent.Create(LMethod, FTypeInfo, i);    FEvents.Add(E);  end;end;class constructor TDioInterfaceDispatcher<T>.Create;var  LType: TRttiType;  FContext: TRttiContext;begin  FTypeInfo := TypeInfo(T);  LType := FContext.GetType(FTypeInfo);  FIID := TRttiInterfaceType(LType).GUID;  FMethods := LType.GetMethods();  //Assert(Length(FMethods) <= 30, '只能分发30个以内函数的接口!');end;destructor TDioInterfaceDispatcher<T>.Destroy;var  i: Integer;begin  FSource := nil;  FVirtualSource:=nil;  FVirtualInterface := nil;  FList.DisposeOf;  FEvents.DisposeOf;  inherited;end;function TDioInterfaceDispatcher<T>.GetSource: T;begin  Result := FSource;end;procedure TDioInterfaceDispatcher<T>.RemoveObserver(const aListener: T);var  N, i: Integer;begin  N := FList.IndexOf(aListener);  if N >= 0 then  begin    for i := 0 to FEvents.Count - 1 do      FEvents[i].Delete(N);  end;  FList.Remove(aListener)end;end.

 

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台