本文来自: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)

  1. procedure TForm2.TreeView1Change(Sender: TObject; Node: TTreeNode);
  2. var
  3. L:TTVChangedEvent;
  4. begin
  5. for L in FList do //FList:TList<TTVChangedEvent>
  6. L(Sender, Node);
  7. end;

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

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

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

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

2.

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

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

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

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

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

其中:

(1).

  1. CurrentStateDispatcher.Source:ICurrentStateObserver
  1. 是一个虚拟接口,也是ICurrentStateObserver类型。调用此接口内的方法,就自动调用所有观察者所对应方法。
  1. 这样我们只需要调用
  1. CurrentStateDispatcher.Source.OnCurrentObjectChange(...);
  2. CurrentStateDispatcher.Source.OnDataReceive(...);
  3. CurrentStateDispatcher.Source.OnResize(...);
  1. 就可以实现所有观察者的调用。
  2.  
  3. (2).
  1. CurrentStateDispatcher:IInterfaceObservable<ICurrentStateObserver>
  1. IInterfaceObservable<ICurrentStateObserver>是一个实现ICurrentStateObserver的多播监听器模式(观察者模式)的接口:
  1. IInterfaceObservable < T: IInterface >= interface
  2. procedure AddObserver(const aListener: T);
  3. procedure RemoveObserver(const aListener: T);
  4. function GetSource: T;
  5. property Source: T read GetSource;
  6. end;

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

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

  1. (3).在使用模式的对象中声明:
  1. TForm2=class(TForm)
  2. ....
  3. private
  4. FCurrentStateDispatcher:IInterfaceObservable<ICurrentStateObserver>;
  5. public
  6. property CurrentStateDispatcher:IInterfaceObservable<ICurrentStateObserver> read FCurrentStateDispatcher;
  7. end;
  1. 3.
  1. 下面我们看一个完整的使用例子:
    uMainForm.pas
  1. unit uMainForm;
  2.  
  3. interface
  4.  
  5. uses
  6. Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  7. Vcl.Controls, Vcl.Forms, Vcl.Dialogs , uInterfaceObservable, Vcl.StdCtrls,
  8. Vcl.ComCtrls;
  9.  
  10. type
  11. {$M+}
  12. ITestObserver=interface
  13. ['{FE7F7C11-13BC-472A-BB7A-6536E20BCEDD}']
  14. procedure OnClick(Sender:TObject);
  15. procedure OnResize(Sender:TObject; X, Y:Integer);
  16. end;
  17. {$M-}
  18.  
  19. TForm2=class;
  20. TObserver1=class(TInterfacedObject, ITestObserver)
  21. F:TForm2;
  22. procedure OnClick(Sender:TObject);
  23. procedure OnResize(Sender:TObject; W, H:Integer);
  24. constructor Create(Owner:TForm2);
  25. end;
  26.  
  27. TObserver2=class(TInterfacedObject, ITestObserver)
  28. F:TForm2;
  29. procedure OnClick(Sender:TObject);
  30. procedure OnResize(Sender:TObject; W, H:Integer);
  31. constructor Create(Owner:TForm2);
  32. end;
  33.  
  34. TForm2 = class(TForm)
  35. Memo2: TMemo;
  36. procedure FormClick(Sender: TObject);
  37. procedure FormResize(Sender: TObject);
  38. procedure FormCreate(Sender: TObject);
  39. procedure FormDestroy(Sender: TObject);
  40. private
  41. { Private declarations }
  42. FTestDispatcher:IInterfaceObservable<ITestObserver>;
  43. public
  44. { Public declarations }
  45. property TestDispatcher:IInterfaceObservable<ITestObserver> read FTestDispatcher;
  46. end;
  47.  
  48. var
  49. Form2: TForm2;
  50.  
  51. implementation
  52.  
  53. {$R *.dfm}
  54.  
  55. procedure TForm2.FormClick(Sender: TObject);
  56. begin
  57. FTestDispatcher.Source.OnClick(Sender);
  58. end;
  59.  
  60. procedure TForm2.FormCreate(Sender: TObject);
  61. begin
  62. FTestDispatcher:=TDioInterfaceDispatcher<ITestObserver>.Create;
  63. FTestDispatcher.AddObserver(TObserver1.Create(Self));
  64. FTestDispatcher.AddObserver(TObserver2.Create(Self));
  65. end;
  66.  
  67. procedure TForm2.FormDestroy(Sender: TObject);
  68. begin
  69. FTestDispatcher:=nil;
  70. end;
  71.  
  72. procedure TForm2.FormResize(Sender: TObject);
  73. var
  74. // i:Integer;
  75. // T:LongWord;
  76. W, H:Integer;
  77. begin
  78. W:=Width;
  79. H:=Height;
  80. // T:=GetTickCount;
  81. // for i := 0 to 1000000 do
  82. TestDispatcher.Source.OnResize(Sender, W, H);
  83. // ShowMessage(IntToStr(GetTickCount- T));
  84. end;
  85.  
  86. { TObserver1 }
  87.  
  88. constructor TObserver1.Create(Owner: TForm2);
  89. begin
  90. F:=Owner;
  91. end;
  92.  
  93. procedure TObserver1.OnClick(Sender: TObject);
  94. begin
  95. F.Memo2.Lines.Add('TObserver1.OnClick');
  96. end;
  97.  
  98. procedure TObserver1.OnResize(Sender: TObject; W, H:Integer);
  99. begin
  100. F.Memo2.Lines.Add(Format('TObserver1.OnResize:%d, %d', [W, H]));
  101. end;
  102.  
  103. { TObserver2 }
  104.  
  105. constructor TObserver2.Create(Owner: TForm2);
  106. begin
  107. F:=Owner;
  108. end;
  109.  
  110. procedure TObserver2.OnClick(Sender: TObject);
  111. begin
  112. F.Memo2.Lines.Add('TObserver2.OnClick');
  113. end;
  114.  
  115. procedure TObserver2.OnResize(Sender: TObject; W, H:Integer);
  116. begin
  117. F.Memo2.Lines.Add(Format('TObserver2.OnResize:%d, %d', [W, H]));
  118. end;
  119.  
  120. end.

uMainForm.dfm

  1. object Form2: TForm2
  2. Left =
  3. Top =
  4. Caption = 'Form2'
  5. ClientHeight =
  6. ClientWidth =
  7. OnClick = FormClick
  8. OnCreate = FormCreate
  9. OnDestroy = FormDestroy
  10. OnResize = FormResize
  11. TextHeight =
  12. object Memo2: TMemo
  13. Left =
  14. Top =
  15. Width =
  16. Height =
  17. Align = alBottom
  18. TabOrder =
  19. end
  20. end

4.

下面是uInterfaceObservable.pas

  1. unit uInterfaceObservable;
  2.  
  3. interface
  4.  
  5. uses System.Generics.Collections, System.TypInfo, System.Rtti;
  6.  
  7. type
  8. IInterfaceObservable < T: IInterface >= interface
  9. procedure AddObserver(const aListener: T);
  10. procedure RemoveObserver(const aListener: T);
  11. function GetSource: T;
  12. property Source: T read GetSource;
  13. end;
  14.  
  15. TDioInterfaceDispatcher<T: IInterface> = class(TInterfacedObject, IInterfaceObservable<T>)
  16. protected
  17. class var FTypeInfo: PTypeInfo;
  18. class var FMethods: TArray<TRttiMethod>;
  19. class var FIID: TGUID;
  20. class constructor Create;
  21. protected
  22. FList: TList<T>;
  23. FVirtualSource, FSource: T;
  24. FVirtualInterface: TVirtualInterface;
  25. FEvents: TObjectList<TList<TMethod>>;
  26. procedure MethodInvoke(Method: TRttiMethod; const Args: TArray<TValue>;
  27. out Result: TValue);
  28. public
  29. procedure AddObserver(const aListener: T);
  30. procedure RemoveObserver(const aListener: T);
  31. function GetSource: T;
  32. constructor Create;
  33. destructor Destroy; override;
  34. property Source: T read FSource;
  35. end;
  36.  
  37. implementation
  38.  
  39. uses System.SysUtils;
  40.  
  41. { TDioDispatcher<T> }
  42.  
  43. procedure TDioInterfaceDispatcher<T>.AddObserver(const aListener: T);
  44. type
  45. TVtable = array [ .. ] of Pointer;
  46. PVtable = ^TVtable;
  47. PPVtable = ^PVtable;
  48. var
  49. i: Integer;
  50. M: TMethod;
  51. P: Pointer;
  52. begin
  53. FList.Add(aListener);
  54. P:=IInterface(aListener);
  55. // P := IInterfaceGetObject(aListener).GetObject;
  56. for i := to FEvents.Count - do
  57. begin
  58. // 3 is offset of Invoke, after QI, AddRef, Release
  59. M.Code := PPVtable(P)^^[ + i ] ;
  60. M.Data := P;
  61. FEvents[i].Add(M);
  62. end;
  63. if FList.Count= then
  64. FSource:=aListener
  65. else
  66. FSource:=FVirtualSource;
  67. end;
  68.  
  69. procedure TDioInterfaceDispatcher<T>.MethodInvoke(Method: TRttiMethod;
  70. const Args: TArray<TValue>; out Result: TValue);
  71. var
  72. L:TList<TMethod>;
  73. M:TMethod;
  74. i:Integer;
  75. begin
  76. L:=FEvents[Method.VirtualIndex-];
  77. i:=;
  78. while i<L.Count do
  79. begin
  80. M:=L[i];
  81. Args[]:=M.Data;
  82. System.Rtti.Invoke(M.Code, Args, Method.CallingConvention, nil);
  83. if (M=L[i]) then
  84. Inc(i);
  85. end;
  86. end;
  87.  
  88. constructor TDioInterfaceDispatcher<T>.Create;
  89. var
  90. i: Integer;
  91. LMethod: TRttiMethod;
  92. E: TList<TMethod>;
  93. S:String;
  94. begin
  95. inherited Create;
  96. FEvents := TObjectList<TList<TMethod>>.Create(True);
  97. FList := TList<T>.Create;
  98. FVirtualInterface := TVirtualInterface.Create(FTypeInfo);
  99. FVirtualInterface.OnInvoke := Self.MethodInvoke;
  100. FVirtualInterface.QueryInterface(FIID, FVirtualSource);
  101. Assert(Assigned(FVirtualSource), '未找到接口' + GUIDToString(FIID));
  102. FSource:=FVirtualSource;
  103. for i := to High(FMethods) do
  104. begin
  105. E := TList<TMethod>.Create;//TEvent.Create(LMethod, FTypeInfo, i);
  106. FEvents.Add(E);
  107. end;
  108. end;
  109.  
  110. class constructor TDioInterfaceDispatcher<T>.Create;
  111. var
  112. LType: TRttiType;
  113. FContext: TRttiContext;
  114. begin
  115. FTypeInfo := TypeInfo(T);
  116. LType := FContext.GetType(FTypeInfo);
  117. FIID := TRttiInterfaceType(LType).GUID;
  118. FMethods := LType.GetMethods();
  119. //Assert(Length(FMethods) <= , '只能分发30个以内函数的接口!');
  120. end;
  121.  
  122. destructor TDioInterfaceDispatcher<T>.Destroy;
  123. var
  124. i: Integer;
  125. begin
  126. FSource := nil;
  127. FVirtualSource:=nil;
  128. FVirtualInterface := nil;
  129. FList.DisposeOf;
  130. FEvents.DisposeOf;
  131. inherited;
  132. end;
  133.  
  134. function TDioInterfaceDispatcher<T>.GetSource: T;
  135. begin
  136. Result := FSource;
  137. end;
  138.  
  139. procedure TDioInterfaceDispatcher<T>.RemoveObserver(const aListener: T);
  140. var
  141. N, i: Integer;
  142. begin
  143. N := FList.IndexOf(aListener);
  144. if N >= then
  145. begin
  146. for i := to FEvents.Count - do
  147. FEvents[i].Delete(N);
  148. end;
  149. FList.Remove(aListener)
  150. end;
  151.  
  152. end.

Delphi的基于接口(IInterface)的多播监听器模式(观察者模式 )的更多相关文章

  1. Eclipse 基于接口编程的时候,快速跳转到实现类的方法(图文)

    Eclipse 基于接口编程的时候,要跳转到实现类很麻烦,其实Eclipse已经实现该功能. 只要按照Ctrl键,把鼠标的光标放在要跳转的方法上面,第一个是跳转到接口里面,第二个方法是跳转到实现类的位 ...

  2. mybatis整合spring 之 基于接口映射的多对一关系

    转载自:http://my.oschina.net/huangcongmin12/blog/83731 mybatis整合spring 之  基于接口映射的多对一关系. 项目用到俩个表,即studen ...

  3. 【java爬虫】---爬虫+基于接口的网络爬虫

    爬虫+基于接口的网络爬虫 上一篇讲了[java爬虫]---爬虫+jsoup轻松爬博客,该方式有个很大的局限性,就是你通过jsoup爬虫只适合爬静态网页,所以只能爬当前页面的所有新闻.如果需要爬一个网站 ...

  4. 基于接口回调详解JUC中Callable和FutureTask实现原理

    Callable接口和FutureTask实现类,是JUC(Java Util Concurrent)包中很重要的两个技术实现,它们使获取多线程运行结果成为可能.它们底层的实现,就是基于接口回调技术. ...

  5. was集群下基于接口分布式架构和开发经验谈

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/luozhonghua2014/article/details/34084935    某b项目是我首 ...

  6. Net系列框架-Dapper+AutoFac 基于接口

    Net系列框架-Dapper+AutoFac 基于接口 工作将近6年多了,工作中也陆陆续续学习和搭建了不少的框架,后续将按由浅入深的方式,整理出一些框架源码,所有框架源码本人都亲自调试通过,如果有问题 ...

  7. 最简单的动态代理实例(spring基于接口代理的AOP原理)

    JDK的动态代理是基于接口的 package com.open.aop; public interface BusinessInterface {     public void processBus ...

  8. Spring AOP 介绍与基于接口的实现

    热烈推荐:超多IT资源,尽在798资源网 声明:转载文章,为防止丢失所以做此备份. 本文来自公众号:程序之心 原文地址:https://mp.weixin.qq.com/s/vo94gVyTss0LY ...

  9. 配置基于接口地址池的DHCP

    配置基于接口地址池的DHCP 原理概述 DHCP(动态主机配置协议),采用C/S方式工作,C向S动态请求配置信息,S自动分配配置信息. 基于接口地址池的DHCP服务器,链接这个接口网段的用户都可以从该 ...

随机推荐

  1. 转:NLog 自定义日志内容,写日志到数据库;修改Nlog.config不起作用的原因

    转:http://www.cnblogs.com/tider1999/p/4308440.html NLog的安装请百度,我安装的是3.2.NLog可以向文件,数据库,邮件等写日志,想了解请百度,这里 ...

  2. android:#FFFFFFFF 颜色码解析

    原文地址:android:#FFFFFFFF 颜色作者:android小鸟 颜色色码为#FFFFFFFF 其中颜色顺序依次为#AARRGGBB 前两位AA代表透明度,FF时表示不透明,00表示透明: ...

  3. ubuntu解决libstdc++.so.6: cannot open shared object file: No such file or directory:问题

    解决libstdc++.so.6: cannot open shared object file: No such file or directory:原因在于,在13.10 版本中,ia32_lib ...

  4. 迭代器iterator(三):Listlterator遍历arraylist,并用逆序输出结果

    迭代器(iterator) 是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址.迭代器修改了常规指针的接口,所谓迭代器是一种概 念上的抽象:那些行为上像迭 ...

  5. [linux]CentOS无法使用epel源

    [linux]CentOS无法使用epel源 问题的产生与解决 作者作为Android应用开发者,对linux的接触一直是ubuntu为主,但是有一个用于科学上网的vps,由于内存只有64M,所以使用 ...

  6. 【读书笔记】iOS-GCD-block-后台运行

    当一个app按home键退出的时候,只有最多5秒的时间做一些保存或清理资源的工作.但是调用beginBackgroundTaskWithExpirationHandler方法,可以最多有10分时间在后 ...

  7. UITableView删除添加和移动

    #import "RootTableViewController.h" @interface RootTableViewController () @property (nonat ...

  8. Java从零开始学四十四(多线程)

    一.进程与线程 1.1.进程 进程是应用程序的执行实例. 进程是程序的一次动态执行过程,它经历了从代码加载.执行到执行完毕的一个完整过程,这个过程也是进程本身从产生.发展到最终消亡的过程 特征: 动态 ...

  9. 【转】self.myOutlet=nil、viewDidUnload、dealloc的本质剖析

    对于iphone开发人员来说,内存管理是极为重要的技巧,哪怕程序的功能再强大,设计再漂亮,如果内存控制不好,也难逃程序莫名退出的噩运,这与网页开发是完全不同的. 内存控制里面有很多门道,在这里分析一下 ...

  10. 关于git

    一.Git基础教程  01.[入门练习]廖雪峰 git教程网:http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8 ...