您的位置:首页 > 编程语言 > Delphi

Interface 在DELPHI里的用法示例(转贴)

2006-02-21 12:24 316 查看
任务描述:
我们有4个类,分别生成4个对象:A、B、C、D

A需要调用B的方法,把数据送给B;
然后,B调用C的方法,把A送来的数据送给C;
然后,C调用D的方法,把B送来的数据送给D;

然后,当D使用完这个数据后,D要调用C的方法将处理结果通知C;
C再把处理结果通知B;
B再把处理结果通知A;

要求:
写A、B、C、D四个类的时候,要避免对象之间的紧密偶合。按传统做法,大概是让A、B、C、D四个类从同一个实现了发送数据和通知这两个方法的父类继承下来。如果因为某种原因,假设B类必须从其它父类继承,就没办法了。
要做到:1. 每个类都不用考虑从哪个父类继承;2. 每个类都不知道别的类。在写B类的时候,不知道有A类或有C类的存在,不引用定义A类或B类的单元。

在Delphi里,我们可以使用Interface来达到这个目的。

首先,单独采用一个单元来定义Interface:

unit Unit2;

interface

uses Classes;

type
IMySend2=interface
procedure SendData(AData:string; AEventList:TInterfaceList);
end;

IMyEvent2=interface
procedure SendDataSuccess(AData:string; AEventList:TInterfaceList);
end;

implementation

end.

所有的A、B、C、D几个类,都只需要知道Unit2这个单元就行了。它们之间互相不用知道。

每个实现上述接口方法的类,都把自己的通知接口加到AEventList:TInterfaceList里去。最后,每个类在被通知到的时候,都可以根据传来的这个通知接口List,取得发数据给自己的类的通知接口,来通知到应该被通知的类。

下面的代码是实现接口的类。需要注意的是,每个类完全可以写在不同的单元里,单元之间不用互相引用,每个类不用知道其它类。

unit Unit5;
{---------------------------------------------------------
实验:A类调用B类实现的接口方法,把数据和A类自己实现的通知接口方法发送给B;
B类再将数据和B类自己的通知接口方法发送给C;C类再发送给D
最后,D类再通知回C,C通知回B,B通知回A
数据流经过的每个类,都把自己的通知接口方法指针加入到一个LIST中去,传给下一级
--------------------------------------------------------------------------}
interface

uses Unit2,Dialogs, SysUtils,Classes;

type
TClassD=class(TInterfacedObject,IMySend2) //最后一个接收数据的类
public
procedure SendData(AData:string; AEvenTInterfaceList:TInterfaceList);
end;

TClassC=class(TInterfacedObject,IMySend2,IMyEvent2)
public
FMySend2:IMySend2;
procedure SendData(AData:string; AEvenTInterfaceList:TInterfaceList);
procedure SendDataSuccess(AData:string; AEvenTInterfaceList:TInterfaceList);//IMYEvent2
end;

TClassB=class(TInterfacedObject,IMySend2,IMyEvent2)
public
FMySend2:IMySend2;
procedure SendData(AData:string; AEvenTInterfaceList:TInterfaceList);
procedure SendDataSuccess(AData:string; AEvenTInterfaceList:TInterfaceList);//IMYEvent2
end;

TClassA=class(TInterfacedObject,IMyEvent2) //第一个发数据的类
public
FMySend:IMySend2; //这是C实现的接口

procedure SendDataSuccess(AData:string; AEvenTInterfaceList:TInterfaceList);//IMYEvent2
procedure DoSend(AData:string); //由程序调用,主动发出数据给C
end;

implementation

{ TClassA }

{ TClassD }

procedure TClassD.SendData(AData: string; AEvenTInterfaceList: TInterfaceList);
var
AEvent:IMyEvent2;
begin
if AEvenTInterfaceList.Count>0 then
begin
AEvent:=IMyEvent2(AEvenTInterfaceList[AEvenTInterfaceList.Count-1]); //取出最近的一条接口
AEvenTInterfaceList.Delete(AEvenTInterfaceList.Count-1); //将此接口从LIST里删除
AEvent.SendDataSuccess(AData,AEvenTInterfaceList); //调用此接口通知前一个对象,并将List传给那个对象
AEvent:=nil;
end;
end;

{ TClassC }

procedure TClassC.SendData(AData: string; AEvenTInterfaceList: TInterfaceList);
var
MyEvenTInterfaceList:TInterfaceList;
begin
//此方法被B调用来输入数据,并在这里调用D类的接口将数据送给D
//FMySend2 由程序来将D的接口放进来。
if Assigned(AEvenTInterfaceList) then
begin
MyEvenTInterfaceList:=AEvenTInterfaceList;
end
else
begin
MyEvenTInterfaceList:=TInterfaceList.Create;
end;
MyEvenTInterfaceList.Add(IMyEvent2(self)); //把自己的通知接口加到列表里去,送给下一级。

FMySend2.SendData(AData+'C',MyEvenTInterfaceList);
end;

procedure TClassC.SendDataSuccess(AData: string;
AEvenTInterfaceList: TInterfaceList);
var
AEvent:IMyEvent2;
begin
ShowMessage('C类被通知= '+AData);
if AEvenTInterfaceList.Count>0 then
begin
AEvent:=IMyEvent2(AEvenTInterfaceList.Items[AEvenTInterfaceList.Count-1]);
AEvenTInterfaceList.Delete(AEvenTInterfaceList.Count-1);
AEvent.SendDataSuccess(AData,AEvenTInterfaceList);
AEvent:=nil;
end;
end;

{ TClassB }

procedure TClassB.SendData(AData: string; AEvenTInterfaceList: TInterfaceList);
var
MyEvenTInterfaceList:TInterfaceList;
begin
//此方法被B调用来输入数据,并在这里调用D类的接口将数据送给D
//FMySend2 由程序来将D的接口放进来。
if Assigned(AEvenTInterfaceList) then
begin
MyEvenTInterfaceList:=AEvenTInterfaceList;
end
else
begin
MyEvenTInterfaceList:=TInterfaceList.Create;
end;
MyEvenTInterfaceList.Add(IMyEvent2(self)); //把自己的通知接口加到列表里去,送给下一级。

FMySend2.SendData(AData+'B',MyEvenTInterfaceList);
end;

procedure TClassB.SendDataSuccess(AData: string;
AEvenTInterfaceList: TInterfaceList);
var
AEvent:IMyEvent2;
begin
ShowMessage('B类被通知= '+AData);
if AEvenTInterfaceList.Count>0 then
begin
AEvent:=IMyEvent2(AEvenTInterfaceList.Items[AEvenTInterfaceList.Count-1]);
AEvenTInterfaceList.Delete(AEvenTInterfaceList.Count-1);
AEvent.SendDataSuccess(AData,AEvenTInterfaceList);
AEvent:=nil;
end;
end;

{ TClassA }

procedure TClassA.DoSend(AData:string);
var
MyEvenTInterfaceList:TInterfaceList;
begin
MyEvenTInterfaceList:=TInterfaceList.Create;
MyEvenTInterfaceList.Add(IMyEvent2(self)); //一定要加强制类型转换!
FMySend.SendData(AData,MyEvenTInterfaceList);
end;

procedure TClassA.SendDataSuccess(AData: string;
AEvenTInterfaceList: TInterfaceList);
var
AEvent:IMyEvent2;
i:Integer;
begin
ShowMessage('A类被通知'+AData);
if AEvenTInterfaceList.Count>0 then
begin
ShowMessage('接口列表里还有接口指针,奇怪!');
for i:=AEvenTInterfaceList.Count-1 downto 0 do
begin
AEvent:=IMyEvent2(AEvenTInterfaceList.Items[i]);
AEvent:=nil;
AEvenTInterfaceList.Delete(i);
end;
end
else
begin
ShowMessage('接口列表里没有接口指针了。');
end;
AEvenTInterfaceList.Free;
end;

end.

最后,写一个程序来测试结果,会看到,的确符合程序里给的调用顺序:

procedure TForm1.Button5Click(Sender: TObject);
var
AClassA:TClassA;
AClassB:TClassB;
AClassC:TClassC;
AClassD:TClassD;
begin
AClassA:=TClassA.Create;
AClassB:=TClassB.Create;
AClassC:=TClassC.Create;
AClassD:=TClassD.Create;

AClassA.FMySend:=IMySend2(AClassB);
AClassB.FMySend2:=IMySend2(AClassC);
AClassC.FMySend2:=IMySend2(AClassD);

AClassA.DoSend('你好吗?');
end;

顺便提一下,使用接口还有一个好处,是当增加一个F类,而且把F类插到数据传递的中间,比如A->B->F->C->D,然后D处理完后通知顺序是D->C->F->B->A,完全不用更改其它类。仅仅需要增加一个实现了上述两个接口的F类,然后在程序里把F类插进去就可以了。这样做应该是对程序的改动最小的一种方法了。

这里值得注意的一个语法问题:
上面有这样的语句:AClassA.FMySend:=IMySend2(AClassB); 在语法上来看,可以写成这样:AClassA.FMySend:=AClassB;完全没问题。一样可以在ClassA里调用到ClassB里的接口方法。

但是,如果把接口加到TInterfaceList里去,就好象上面的这句:MyEvenTInterfaceList.Add(IMyEvent2(self)),如果写成:
MyEvenTInterfaceList.Add(self),编译完全没问题,能跑起来。但是,当从TInterfaceList里把接口取出来的时候,也就是执行这一句:AEvent:=IMyEvent2(AEvenTInterfaceList[AEvenTInterfaceList.Count-1])的时候,就会出AV错误。

结论:当把接口加入到TInterfaceList里的时候,一定要做强制类型转换,转换为你要的接口!然后取出来的时候才会正确。但当把接口作为一个类的私有变量的时候,可以直接把实现该接口的类指给它也可以
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: