Delphi事件的广播2

原创 2006年05月13日 14:44:00

上篇文章写了将事件分离成类的方法来实现事件的广播,这次将参考观察者模式来实现事件的广播。模式中主要有这两个角色:

发布者:发布者保存着一张观察者的列表,以便在必要的时候调用观察者的方法。

观察者:观察者是现实某些特定接口的类,对于发布者来说,它只关注这些接口,并不关注观察者具体是什么类。

为了让发布者更具通用性,我写了一个发布者的父类,它负责增删和管理观察者,一个类只要继续这个类,马上就有了发布者的特征,因此你也可以将这个单元作为你的发布者父类。看下面代码:

unit EventSubject;

interface
uses
  Classes;
type
  
//事件发布者的基类
  TEventSubject = class
  protected
    FObservers: IInterfaceList;
  public
    
//增加一个观察者
    procedure AddObserver(const Observer: IInterface);
    
//移除一个观察者
    procedure RemoveObserver(const Observer: IInterface);
    constructor Create;
    destructor Destroy; override;
  end;
implementation

{ TEventSubject }

procedure TEventSubject.AddObserver(const Observer: IInterface);
begin
  if FObservers.IndexOf(Observer) < 
0 then
    FObservers.Add(Observer);
end;

constructor TEventSubject.Create;
begin
  FObservers := TInterfaceList.Create;
end;

destructor TEventSubject.Destroy;
begin
  FObservers := nil;
  inherited;
end;

procedure TEventSubject.RemoveObserver(const Observer: IInterface);
begin
  FObservers.Remove(Observer);
end;

end.

    接下来是否将Rectangle类直接继续自EventSubject,我进行了一些思考,最后还是决定分离成一个独立的类,这样类的职责更加分明一些。不过之前得声明一个接口,这个接口提供了矩形事件的服务:

//矩形事件的接口
  IRectEvent = Interface(IInterface)
    [
'{C9FAFE6C-3C51-4B3F-9E73-E8EA898D4061}']
    procedure OnRectChange(Rectangle: TRectangle);
    procedure BeforeRectChange(Rectangle: TRectangle);
  end;

    而矩形事件发布者的实现相当的简单,只是遍历父类的FObservers列表,一一调用IRectEvent接口的方法:

//矩形事件发布者类
TRectEventSubject = class(TEventSubject)
public
  procedure DoRectChange(Rectangle: TRectangle);
  procedure BeforeRectChange(Rectangle: TRectangle);
end;

... ...

{ TRectEventSubject }

procedure TRectEventSubject.BeforeRectChange(Rectangle: TRectangle);
var
  i: Integer;
begin
  for i := 
0 to FObservers.Count - 1 do
    if Supports(FObservers[i], IRectEvent) then
      (FObservers[i] as IRectEvent).BeforeRectChange(Rectangle);
end;

procedure TRectEventSubject.DoRectChange(Rectangle: TRectangle);
var
  i: Integer;
begin
  for i := 
0 to FObservers.Count - 1 do
    if Supports(FObservers[i], IRectEvent) then
      (FObservers[i] as IRectEvent).OnRectChange(Rectangle);
end;

    上面有一点要注意的是,由于FObservers保存的是一张IInterface的列表,所以必须调用Supports方法判断该接口是否为IRectEvent,才能进行转接和调用。

    矩形事件发布者类完成之后,即可将原来的事件广播类和事件触发类去掉,因为这两个类的职责已经由矩形事件发布者类代替了。然后在Rectangle类中声明一个TRectEventSubject成员并引出一个属性。最后我把Rectangle全局对象从MainFrm中移到自己的单元中,在初始化节中创建和在结束节中释放,毕竟我们认为矩形类是一开始就有的吗,这样更不依赖于外部的界面:

initialization
  Rectangle := TRectangle.Create;
finalization
  Rectangle.Free;

end.

    完成了wdRect单元的改造,接下来修改界面相关的单元。首先是主窗口,这里只剩下初始化矩形大小的代码了:

procedure TfrmMain.FormCreate(Sender: TObject);
begin
  
//初始化画布的属性
  Rectangle.Width := 
100;
  Rectangle.Height := 
100;
end;

    而画布单元和矩形信息单元呢,这两个相当于观察者的具体类,所以它们要实现IRectEvent接口,并有要声明和实现接口的两个方法,这里只以DrawFrame为例,看下面代码:

type
  TfmeDraw = class(TFrame, IRectEvent)
    ... ...
    
//IRectEvent
    procedure OnRectChange(Rectangle: TRectangle);
    procedure BeforeRectChange(Rectangle: TRectangle);
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;

implementation

{$R *.dfm}

{ TfmeDraw }

... ...

procedure TfmeDraw.OnRectChange(Rectangle: TRectangle);
begin
  Rectangle.Draw(imgDraw.Canvas);
end;

procedure TfmeDraw.BeforeRectChange(Rectangle: TRectangle);
begin
  Rectangle.Erase(imgDraw.Canvas);
end;

constructor TfmeDraw.Create(AOwner: TComponent);
begin
  inherited;
  Rectangle.RectEventSubject.AddObserver(IInterface(Self));
end;

destructor TfmeDraw.Destroy;
begin
  Rectangle.RectEventSubject.RemoveObserver(IInterface(Self));
  inherited;
end;

end.

    我省略了很多不相关的代码,首先它是在构造方法中将自己加进RectEventSubject中,使之成为一个观察者;在构造方法中又将自己从RectEventSubject从移除,不知有人会不会有疑问:现实接口的对象的生命周期会由接口管理,那么fmeDraw的释放会不会由IRectEvent管理呢,答案是不会,具体原因请看我的另一篇文章:接口小论。上面代码中另外两个方法即是实现IRectEvent的方法,作用和上篇的事件是一样的。

    TfmeInfo也遵循了相同的规则实现IRectEvent接口,不过有一点是必须注意的,现实接口的类必须实现接口中所有的方法,FmeInfo不能象上篇一样只得到OnRectChange的事件,它还要实现BeforeRectChange方法,不过既然没有用,把BeforeRectChange当成一个空方法就行了,也并不伤大雅。

    至此,程序改造完毕,可以看到,改动其实并不大,不过与第一种方法相比,用Observer模式性能要低一些,拉动画布中的矩形,可以明显看到矩形的闪动。

    用哪一种方法更好其实看具体应用,我个人更喜欢第一种方法,主要是性能要高一些,另外灵活性也并不输Observer模式。可见模式都要看具体的应用,也不能生搬硬套吧。而模式当然也不是死的,自己再多些思考,也许能找出比这两种更好的方法,期待你的发现,如果你有更好的方法,可以在留言中告知,如果你要完整的Demo,可以发邮件给我,我很乐意与你交流。

下一篇敬请期待,Bye!

delphi中Message消息的使用方法

原文:http://blog.csdn.net/delphi1234/article/details/2110083 实例1 unit Unit1;interfaceuses Win...
  • fhfanxin
  • fhfanxin
  • 2016年03月06日 16:35
  • 1320

delphi消息广播的处理.rar

  • 2011年08月05日 16:53
  • 474KB
  • 下载

Delphi Android 广播事件处理

  • 2017年07月06日 20:34
  • 9.03MB
  • 下载

Delphi事件的广播

    明天就是五一节了,辛苦了好几个月,借此机会应该尽情放松一番。可是想到Blog好久没有写文章,似乎缺些什么似的。这几个月来在项目中又增长了许多经验,学到许多实际应用的知识。不如把一些比较有用的记...
  • linzhengqun
  • linzhengqun
  • 2006年05月07日 13:37
  • 4298

Delphi事件的广播

    明天就是五一节了,辛苦了好几个月,借此机会应该尽情放松一番。可是想到Blog好久没有写文章,似乎缺些什么似的。这几个月来在项目中又增长了许多经验,学到许多实际应用的知识。不如把一些比较有用的记...
  • sforiz
  • sforiz
  • 2010年05月09日 11:50
  • 413

delphi中消息的使用

转自:http://blog.csdn.net/delphi1234/article/details/2110083 实例1  unit Unit1; interface uses  W...
  • Yoryky
  • Yoryky
  • 2014年12月22日 09:43
  • 518

广播源代码

  • 2014年07月04日 21:16
  • 128KB
  • 下载

Delphi用Socket IP/UDP局域网广播

 A类互联网网址: 7位网络号          24位主机号               01111111.11111111.11111111.11111111 --->01111111 = 127...
  • aroc_lo
  • aroc_lo
  • 2009年11月26日 13:48
  • 3201

JavaScript的事件广播与侦听

先来看html页面的主要结构: left: right: 需求:id为test的输入框(以下简称A)值改变时,实时改变id为left和right的内容。 如果这是一个小...
  • flm000
  • flm000
  • 2012年09月26日 19:48
  • 3193

delphi中Message消息的使用方法

unit Unit1;      interface   uses   Windows, Messages, SysUtils, Variants, Classes, Graphics, Co...
  • zang141588761
  • zang141588761
  • 2016年05月19日 11:15
  • 910
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Delphi事件的广播2
举报原因:
原因补充:

(最多只允许输入30个字)