This article comes from: http: //www.cnblogs.com/hezihang/p/6083555.html
Interface module using Delphi design can reduce the coupling between the module, to facilitate maintenance and extensions. This article provides an implementation of listener mode (observer mode, a subscriber model) based on the interface (IInterface) way to achieve an automatic multicast device.
The following test program in Berlin by other Delphi versions not tested, not cross-platform test (should be able to support)
1.prepare
Interface using the observer pattern, the correlation function may merge the interface.
Example: Suppose we have a window TTreeView, objects for display applications, the user clicks TreeView different objects, a plurality of different switch the display contents of other windows.
In the traditional way, maintains a notification list, TreeView.OnChange event can be used to call all notifications, and then processed as follows :( can also be used in multicast mode, see: 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;
Obviously the traditional way, the windows need to uses where the window TreeView.
In addition, if there are other events TreeView window where the need for multiple external windows or objects notice,
The need to establish a re-notification list.
Event-mode, you need to maintain your own one or more notification list, while each unit using the events require a reference unit event source.
2.
If we use the interface mode, all events will be included into by the window where the TreeView to call:
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-}
Note that automatically opens a viewer interface must RTTI, {$ M +}
Then, just call the following, you can make all listeners (observer) of OnResize be called (all methods within interfaces can be called):
procedure TForm2.FormResize(Sender: TObject); begin CurrentStateDispatcher.Source.OnResize(Width, Height); end;
among them:
(1).
CurrentStateDispatcher.Source:ICurrentStateObserver
It is a virtual interface, also ICurrentStateObserver type. Within this method call interface, the viewer automatically call all the corresponding method.
We only need to call
CurrentStateDispatcher.Source.OnCurrentObjectChange(...); CurrentStateDispatcher.Source.OnDataReceive(...); CurrentStateDispatcher.Source.OnResize(...);
We can achieve call all the observer.
(2).
CurrentStateDispatcher:IInterfaceObservable<ICurrentStateObserver>
IInterfaceObservable <ICurrentStateObserver> multicast listener mode (observer pattern) to achieve a ICurrentStateObserver interface of:
IInterfaceObservable < T: IInterface >= interface procedure AddObserver(const aListener: T); procedure RemoveObserver(const aListener: T); function GetSource: T; property Source: T read GetSource; end;
AddObserver add listeners, RemoveObject is to remove the observer
Source is the virtual interface for the aforementioned multicast call.
. (3) in the object usage patterns in a statement:
TForm2=class(TForm) .... private FCurrentStateDispatcher:IInterfaceObservable<ICurrentStateObserver>; public property CurrentStateDispatcher:IInterfaceObservable<ICurrentStateObserver> read FCurrentStateDispatcher; end;
3.
Let's look at a complete example of the use:
uMainForm.pas
unit uMainForm; interface uses 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 end end
4.
Here is uInterfaceObservable.pas
unit uInterfaceObservable; interface uses 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; implementation uses 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.