面向对象语言中的回调(Delphi)


Dephi与C 一样,为了保持与过程语言Pascal的兼容性,它在引入面向对象机制的同时,保留了以前的结构化特性。因此,对回调的实现,也有两种截然不同的模式,一种是结构化的函数回调模式,一种是面向对象的接口模式。

  2.4.1 回调函数

  回调函数类型定义:

type 
 
TCalcFunc=function (a:integer;b:integer):integer; 
 
  按照回调函数的格式自定义函数的实现,如 
 

function Add(a:integer;b:integer):integer 
 
begin 
 
 result:=a b; 
 
end; 
 
function Sub(a:integer;b:integer):integer 
 
begin 
 
 result:=a-b; 
 
end;


  回调的使用

function Calc(calc:TcalcFunc;a:integer;b:integer):integer


  下面,我们就可以在我们的程序里按照需要调用这两个函数了

c:=calc(add,a,b);//c=a b 
 
c:=calc(sub,a,b);//c=a-b


  2.4.2 回调对象

  什么叫回调对象呢,它具体用在哪些场合?首先,让我们把它与回调函数对比一下,回调函数是一个定义了函数的原型,函数体则交由第三方来实现的一种动态应用模式。要实现一个回调函数,我们必须明确知道几点:该函数需要那些参数,返回什么类型的值。同样,一个回调对象也是一个定义了对象接口,但是没有具体实现的抽象类(即接口)。要实现一个回调对象,我们必须知道:它需要实现哪些方法,每个方法中有哪些参数,该方法需要放回什么值。

  因此,在回调对象这种应用模式中,我们会用到接口。接口可以理解成一个定义好了但是没有实现的类,它只能通过继承的方式被别的类实现。Delphi中的接口和COM接口类似,所有的接口都继承与IInterface(等同于IUnknow),并且要实现三个基本的方法QueryInterface, _AddRef, 和_Release。

  定义一个接口

type IShape=interface(IInterface) 
 
procedure Draw; 
 
end


  实现回调类

type TRect=class(TObject,IShape) 
 
protected 
 
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; 
 
function _AddRef: Integer; stdcall; 
 
function _Release: Integer; stdcall; 
 
public 
 
procedure Draw; 
 
end; 
 

type TRound=class(TObject,IShape) 
 
protected 
 
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; 
 
function _AddRef: Integer; stdcall; 
 
function _Release: Integer; stdcall; 
 
public 
 
procedure Draw; 
 
end;


  使用回调对象

procedure MyDraw(shape:IShape); 
 
var 
 
shape:IShape; 
 
begin 
 
shape.Draw; 
 
end;


  如果传入的对象为TRect,那么画矩形;如果为TRound,那么就为圆形。用户也可以按照自己的意图来实现IShape接口,画出自己的图形:

MyDraw(Trect.Create); 
 
MyDraw(Tround.Create);


  2.4.3 回调方法

  回调方法(Callback Method)可以看作是回调对象的一部分,Delphi对windows消息的封装就采用了回调方法这个概念。在有些场合,我们不需要按照给定的要求实现整个对象,而只要实现其中的一个方法就可以了,这是我们就会用到回调方法。

  回调方法的定义如下:

TNotifyEvent = procedure(Sender: TObject) of object; 
 
TMyEvent=procedure(Sender:Tobject;EventId:Integer) of object;


  TNotifyEvent 是Delphi中最常用的回调方法,窗体、控件的很多事件,如单击事件、关闭事件等都是采用了TnotifyEvent。回调方法的变量一般通过事件属性的方式来定义,如TCustomForm的创建事件的定义:

property OnCreate: TNotifyEvent read FOnCreate write FOnCreate stored IsForm; 
 
  我们通过给事件属性变量赋值就可以定制事件处理器。 
 

  用户定义对象(包含回调方法的对象): 
 

type TCallback=Class 
 
procedure ClickFunc(sender:TObject); 
 
end; 
 
procedure Tcallback.ClickFunc(sender:TObject); 
 
begin 
 
showmessage('the caller is clicked!'); 
 
end; 
 
  窗体对象: 
 

type TCustomFrm=class(TForm) 
 
public 
 
procedure RegisterClickFunc(cb:procedure(sender:Tobject) of object); 
 
end; 
 

procedure TcustomFrm..RegisterClickFunc(cb:TNotifyEvent); 
 
begin 
 
self.OnClick=cb; 
 
end; 
 
  使用方法: 
 

var 
 
frm:TcustomFrm; 
 
begin 
 
frm:=TcustomFrm.Create(Application); 
 
frm.RegisterClickFunc(Tcallback.Create().ClickFunc); 
 
end;



  3 回调在分布式计算中的应用(CORBA)

  3.1 回调接口模型

  CORBA的消息传递机制有很多种,比如回调接口、事件服务和通知服务等。回调接口的原理很简单,CORBA客户和服务器都具有双重角色,即充当服务器也是客户客户。

  回调接口的反向调用与正向调用往往是同时进行的,如果服务端多次调用该回调接口,那么这个回调接口就变成异步接口了。因此,回调接口在CORBA中常常充当事件注册的用途,客户端调用该注册函数时,客户函数就是回调函数,在此后的调用中,由于不需要客户端的主动参与,该函数就是实现了一种异步机制。

  从CORBA规范我们知道,一个CORBA接口在服务端和客户端有不同的表现形式,在客户端一般使用桩(Stub)文件,服务端则用到框架(Skeleton)文件,接口的规格采用IDL来定义。而回调函数的引入,使得服务端和客户端都需要实现一定的桩和框架。下面是回调接口的实现模型:



  3.1.1 范例

  下面给出了一个使用回调的接口文件,服务端需要实现Server接口的框架,客户端需要实现CallBack的框架:

module cb 
 
{ 
 
 interface CallBack; 
 
 interface Server; 
 

 interface CallBack 
 
 { 
 
  void OnEvent(in long Source,in long msg); 
 
 }; 
 
 interface Server 
 
 { 
 
  long RegisterCB(in CallBack cb); 
 
  void UnRegisterCB(in long hCb); 
 
 }; 
 
};


  客户端首先通过同步方式调用服务端的接口RegistCB,用来注册回调接口CallBack。服务端收到该请求以后,就会保留该接口引用,如果发生某种事件需要向客户端通知的时候就通过该引用调用客户方的OnEvent函数,以便对方及时处理。

口在服务端和客户端有不同的表现形式,在客户端一般使用桩(Stub)文件,服务端则用到框架(Skeleton)文件,接口的规格采用IDL来定义。而回调函数的引入,使得服务端和客户端都需要实现一定的桩和框架。下面是回调接口的实现模型:



  3.1.1 范例

  下面给出了一个使用回调的接口文件,服务端需要实现Server接口的框架,客户端需要实现CallBack的框架:

module cb 
 
{ 
 
 interface CallBack; 
 
 interface Server; 
 

 interface CallBack 
 
 { 
 
  void OnEvent(in long Source,in long msg); 
 
 }; 
 
 interface Server 
 
 { 
 
  long RegisterCB(in CallBack cb); 
 
  void UnRegisterCB(in long hCb); 
 
 }; 
 
};


  客户端首先通过同步方式调用服务端的接口RegistCB,用来注册回调接口CallBack。服务端收到该请求以后,就会保留该接口引用,如果发生某种事件需要向客户端通知的时候就通过该引用调用客户方的OnEvent函数,以便对方及时处理。