Delphi的基于接口(IInterface)的多播监听器模式(观察者模式 )
本文来自: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;
- 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 =
- Top =
- Caption = 'Form2'
- ClientHeight =
- ClientWidth =
- OnClick = FormClick
- OnCreate = FormCreate
- OnDestroy = FormDestroy
- OnResize = FormResize
- TextHeight =
- object Memo2: TMemo
- Left =
- Top =
- Width =
- Height =
- Align = alBottom
- TabOrder =
- end
- end
4.
下面是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 [ .. ] 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 := to FEvents.Count - do
- begin
- // 3 is offset of Invoke, after QI, AddRef, Release
- M.Code := PPVtable(P)^^[ + i ] ;
- M.Data := P;
- FEvents[i].Add(M);
- end;
- if FList.Count= 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-];
- i:=;
- while i<L.Count do
- begin
- M:=L[i];
- Args[]:=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 := 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个以内函数的接口!');
- 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 >= then
- begin
- for i := to FEvents.Count - do
- FEvents[i].Delete(N);
- end;
- FList.Remove(aListener)
- end;
- end.
Delphi的基于接口(IInterface)的多播监听器模式(观察者模式 )的更多相关文章
- Eclipse 基于接口编程的时候,快速跳转到实现类的方法(图文)
Eclipse 基于接口编程的时候,要跳转到实现类很麻烦,其实Eclipse已经实现该功能. 只要按照Ctrl键,把鼠标的光标放在要跳转的方法上面,第一个是跳转到接口里面,第二个方法是跳转到实现类的位 ...
- mybatis整合spring 之 基于接口映射的多对一关系
转载自:http://my.oschina.net/huangcongmin12/blog/83731 mybatis整合spring 之 基于接口映射的多对一关系. 项目用到俩个表,即studen ...
- 【java爬虫】---爬虫+基于接口的网络爬虫
爬虫+基于接口的网络爬虫 上一篇讲了[java爬虫]---爬虫+jsoup轻松爬博客,该方式有个很大的局限性,就是你通过jsoup爬虫只适合爬静态网页,所以只能爬当前页面的所有新闻.如果需要爬一个网站 ...
- 基于接口回调详解JUC中Callable和FutureTask实现原理
Callable接口和FutureTask实现类,是JUC(Java Util Concurrent)包中很重要的两个技术实现,它们使获取多线程运行结果成为可能.它们底层的实现,就是基于接口回调技术. ...
- was集群下基于接口分布式架构和开发经验谈
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/luozhonghua2014/article/details/34084935 某b项目是我首 ...
- Net系列框架-Dapper+AutoFac 基于接口
Net系列框架-Dapper+AutoFac 基于接口 工作将近6年多了,工作中也陆陆续续学习和搭建了不少的框架,后续将按由浅入深的方式,整理出一些框架源码,所有框架源码本人都亲自调试通过,如果有问题 ...
- 最简单的动态代理实例(spring基于接口代理的AOP原理)
JDK的动态代理是基于接口的 package com.open.aop; public interface BusinessInterface { public void processBus ...
- Spring AOP 介绍与基于接口的实现
热烈推荐:超多IT资源,尽在798资源网 声明:转载文章,为防止丢失所以做此备份. 本文来自公众号:程序之心 原文地址:https://mp.weixin.qq.com/s/vo94gVyTss0LY ...
- 配置基于接口地址池的DHCP
配置基于接口地址池的DHCP 原理概述 DHCP(动态主机配置协议),采用C/S方式工作,C向S动态请求配置信息,S自动分配配置信息. 基于接口地址池的DHCP服务器,链接这个接口网段的用户都可以从该 ...
随机推荐
- 转:NLog 自定义日志内容,写日志到数据库;修改Nlog.config不起作用的原因
转:http://www.cnblogs.com/tider1999/p/4308440.html NLog的安装请百度,我安装的是3.2.NLog可以向文件,数据库,邮件等写日志,想了解请百度,这里 ...
- android:#FFFFFFFF 颜色码解析
原文地址:android:#FFFFFFFF 颜色作者:android小鸟 颜色色码为#FFFFFFFF 其中颜色顺序依次为#AARRGGBB 前两位AA代表透明度,FF时表示不透明,00表示透明: ...
- 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 ...
- 迭代器iterator(三):Listlterator遍历arraylist,并用逆序输出结果
迭代器(iterator) 是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址.迭代器修改了常规指针的接口,所谓迭代器是一种概 念上的抽象:那些行为上像迭 ...
- [linux]CentOS无法使用epel源
[linux]CentOS无法使用epel源 问题的产生与解决 作者作为Android应用开发者,对linux的接触一直是ubuntu为主,但是有一个用于科学上网的vps,由于内存只有64M,所以使用 ...
- 【读书笔记】iOS-GCD-block-后台运行
当一个app按home键退出的时候,只有最多5秒的时间做一些保存或清理资源的工作.但是调用beginBackgroundTaskWithExpirationHandler方法,可以最多有10分时间在后 ...
- UITableView删除添加和移动
#import "RootTableViewController.h" @interface RootTableViewController () @property (nonat ...
- Java从零开始学四十四(多线程)
一.进程与线程 1.1.进程 进程是应用程序的执行实例. 进程是程序的一次动态执行过程,它经历了从代码加载.执行到执行完毕的一个完整过程,这个过程也是进程本身从产生.发展到最终消亡的过程 特征: 动态 ...
- 【转】self.myOutlet=nil、viewDidUnload、dealloc的本质剖析
对于iphone开发人员来说,内存管理是极为重要的技巧,哪怕程序的功能再强大,设计再漂亮,如果内存控制不好,也难逃程序莫名退出的噩运,这与网页开发是完全不同的. 内存控制里面有很多门道,在这里分析一下 ...
- 关于git
一.Git基础教程 01.[入门练习]廖雪峰 git教程网:http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8 ...