代码之家  ›  专栏  ›  技术社区  ›  Re0sless

Delphi接口是在子类中继承的吗

  •  4
  • Re0sless  · 技术社区  · 16 年前

    如果我在一个基类上实现一个接口,它会被它的子类继承吗?我知道函数/过程会继承,但是我更感兴趣的是我是否能够将子类强制转换到接口,然后再返回到它的原始类。

    我希望我能做的是将不同基类的对象传递给一个函数,然后在函数中确定那里的类型并根据需要使用它们。

    这是可能的吗?这是正确的方法吗?

    更新

    为了帮助消除任何混乱(或者创建更多的混乱),这里是我想要做的事情(一直到它的核心)。

    界面

        IMyInterFace = interface
       ['{B7203E50-F5CB-4755-9DB1-FB41B7B192C5}'] 
         function MyFunction: Boolean;
       end;
    

    基类

       type TMyObject = class(TInterfacedObject,IMyInterFace)
    

    子类

      type TSubMyObject = class(TMyObject)
    

    另一个班

      type TMyOtherObject = class(TInterfacedObject,IMyInterFace)
    

    然后使用

     procedure foo();
       var obj:TSubMyObject;
     begin
          obj := TSubMyObject.Create();
          SendInterfaceObject(obj);
     end;
    
     procedure bar();
       var obj:TMyOtherObject;
     begin
          obj := TMyOtherObject.Create();
          SendInterfaceObject(obj);
     end;
    
    
    
     procedure SendInterfaceObject(iobj:IMyInterFace);
     begin
         if (iobj is TSubMyObject) then
         begin
            //code here
         end
         else if (iobj is TMyOtherObject) then
         begin
           //code here
         end;
     end;
    

    更新2

    我已经更新了代码,所以显示了我在追求什么更好。

    这个 /代码在这里 部分与传递给它的对象没有什么关系,例如,如果这个类是taccounts,并且传递给了一个temployee对象,那么它可能每周支付一次工资,但是如果是tinvoice,那么它将检查是否需要支付,并且只在截止日期前2天支付。

    临时工/临时工甚至可能来自要求付款的课外班。

    这只是一个例子。

    5 回复  |  直到 16 年前
        1
  •  9
  •   Binary Worrier    16 年前

    是的,接口由子类继承。

    从子类转换到接口是完全可以接受的。

    但是,如果我读错了你的问题,我道歉,但是如果“然后回到它原来的班级”的意思是。…

    你有接口I,A级和B级。 A实现I,B继承A,您可能可以,但实际上不应该从A强制转换为B。

    编辑:

    你想从B到I再回到B。…但是您已经有了对b的引用,如果b是传递给函数的,那么您不需要从i强制转换为b(除非是在讨论不同的对象,那么不,不要这样做)。

    从I到B和从A到B是一样的,你试图建立继承链,这是你不应该做的。需要这样做是一种代码味道,它告诉您应该尝试以不同的方式解决这个问题(可能通过重新设计类(例如,向i添加更多的属性/方法),或者仅仅决定函数只与子类一起工作-与子类“b”一起工作将使您能够访问a&i的所有方法)。

    你能编辑你的问题并添加一些你想做什么的示例代码吗?

    编辑2

     procedure SendInterfaceObject(iobj:IMyInterFace);
     begin
         if (iobj is TSubMyObject) then
         begin
            //code here
         end;
     end;
    

    其中的“if”语句是一个坏主意,并且破坏了OO原则。如果你需要这样做的话

    • 接口定义是 不足,您可能需要添加 接口的类型属性 允许你(如果有财产 = 1)。..)
    • 接口不是 纠正这个问题的解决方案,以及 您应该将引用传递为 TSyMyObjor。

    编辑3:

    @mghie:我同意你的看法,我没有很好地解释的是,有些属性有一些数据,允许函数在那里分支,消除了类型检查的依赖性。someproperty不应该“简单”替换类型检查(例如,将类名放入属性中,然后检查类名),这确实是相同的问题。

    继承接口的子类之间有一些本质的区别。这种差异应该用以下两种方式来表示:

    • 公开一些可以 然后用在臂上

    例如

    if(item.Color = Red) then 
       item.ContrastColor := Blue;
    else
       item.ContrastColor := Red;
    
    • 或通过多态性,例如

    iemployee定义了一个calculatePay方法,tmanager和worker实现了iemployee,每个方法在calculatePay方法中都有不同的逻辑。

    如果目的是做像第一个案例那样的事情,多态性 能够 过度杀伤力(多态性并不能解决所有问题)。

    编辑4

    你说 “这里的//代码部分与传递给它的对象几乎没有关系。… 很抱歉,这个声明是错误的,如果你需要支付给一个雇员,你需要知道他们的1)雇员代码2)他们的工资详细信息3)他们的银行详细信息等,如果你要收取发票,你需要1)发票号2)发票金额3)客户代码收取等等。… 这是多态性的理想场所 .

    让我们假设一个函数接受接口检查,看看“帐户”是否需要对对象做一些事情(例如,支付员工工资、收取发票费用等)。所以我们可以调用函数accountscheck。在账户检查中,您将有一套特定于每个子类的逻辑(支付员工工资,收取发票费用)。…)这是多态性的理想候选者。

    在接口上(或在另一个接口上,或作为子类上的虚拟方法)定义一个“accountscheck”方法。然后,每个派生类都获得自己的帐户检查实现。

    代码从单调的单accountscheck函数中移出,并在每个子类中移动到较小的函数中。这就是代码

    • 更明显的意图(每门课 包含一些逻辑 会计师事务所
    • 你不太可能破坏子类 B的逻辑 C科目表
    • 很容易搞清楚 什么子类B的accountscheck逻辑 是的,你只需要检查20行 小账户代码,不是200 在总帐中)

    有更多的“好理由”,AIONE希望编辑/发布评论,请这样做。

    如果您发现需要在accountscheck的实现之间共享一些逻辑,请创建一些实用程序函数,不要在每个函数中重新实现相同的控制盘。

    多态性是解决问题的方法。

        2
  •  2
  •   skamradt    16 年前

    我的建议是不要针对类强制转换,而是针对另一个接口强制转换。将tmyotherobject更改为:

    type
      IOtherObjectIntf = interface
        ['{FD86EE29-ABCA-4D50-B32A-24A7E71486A7}']
      end;
    
    type 
      TMyOtherObject = class(TInterfacedObject,IMyInterFace,IOtherObjectIntf)
    

    然后把你的其他程序改为:

    procedure SendInterfaceObject(iobj:IMyInterFace);
    begin
      if Supports(iObj,IOtherObjectIntf) then
        begin
          //code here for TMyOtherObject
        end
      else 
        begin
          //code here for other standard implementations
        end;
    end;
    

    这样,tmyotherobject的“自定义”代码也将应用于它的任何子代,而不需要进一步的自定义代码。iotherobjectif接口只用作“是的,我是那些”指示器之一,它允许您的代码正确地分支。当然,这会浪费到另一个guid上……但是有这么多guid,谁会注意到呢?:)

        3
  •  1
  •   Tiago Moraes    16 年前

    接口由子类继承,您可以将对象强制转换为接口,但将接口强制转换为类是不安全的(或建议)。如果需要这样做,您可能使用接口的方式不正确。

        4
  •  1
  •   Community Mohan Dere    8 年前

    似乎对如何理解你的问题有一些疑问,实际上在你的评论中 this answer 你说你想“从B到I到B”。

    这确实是不推荐的,并且只通过使用有关如何在类上实现接口的信息来支持。

    如果我理解正确,那么您要做的就是将接口传递给某个方法,在该方法中,根据接口实现的具体类,执行不同的操作。但是,最好在开始使用接口后继续使用它们。你 能够 让接口有一个返回实现类的方法,但是您不应该对接口在哪个类中实现做任何假设——这会使您损失一些针对接口编程的好处。

    相反,您可以创建不同的接口,并且只在(某些)您的后代类中实现其中的一些接口。然后你可以用 查询接口() 支持() 在传递的接口指针上。对于您的基类,这将返回 ,但对于实现接口的所有子类,它将返回一个指针,该指针允许您只调用它们拥有的方法。

    编辑: 例如,在OmnithreadLibrary中,您将发现:

    IOmniWorker = interface
      ['{CA63E8C2-9B0E-4BFA-A527-31B2FCD8F413}']
      function  GetImplementor: TObject;
      ...
    end;
    

    可以添加到界面中。但同样,imho使用不同的接口要好得多。

        5
  •  1
  •   Name    16 年前

    你不能将一个接口直接投射到一个对象上(这不是接口的目的),但有时能够这样做是非常实际的,以至于你无法抗拒…

    如果您真的想这样做,可以使用mghie直接在imyinterface中给出的“iomniworker”示例:

    IMyInterFace = interface
    ['{B7203E50-F5CB-4755-9DB1-FB41B7B192C5}'] 
      function MyFunction: Boolean;
      function GetImplementor: TObject;
    end;
    

    函数实现如下所示:

    function TMyObject.GetImplementor: TObject;
    begin
      Result := Self;
    end;
    function TMyOtherObject.GetImplementor: TObject;
    begin
      Result := Self;
    end; 
    

    sendInterfaceObject如下所示:

    procedure SendInterfaceObject(const iobj:IMyInterFace);
    begin
      if (iobj.GetImplementor is TSubMyObject) then
      begin
         //code here
      end
      else if (iobj.GetImplementor is TMyOtherObject) then
      begin
        //code here
      end;
    end;
    

    顺便说一下,我添加了一个(非常)小的优化:通过将iobj作为“const”传递给函数,可以避免不必要的引用计数。

    推荐文章