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

任务描述:
我们有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里的时候,一定要做强制类型转换,转换为你要的接口!然后取出来的时候才会正确。但当把接口作为一个类的私有变量的时候,可以直接把实现该接口的类指给它也可以
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值