接口和抽象类只是具有虚拟抽象方法是一回事

本文关键字:虚拟 抽象方法 一回事 抽象类 接口 | 更新日期: 2023-09-27 18:05:39

如果我有一个包含类似类的项目,其中一些可能使用相同的实现,但在大多数情况下,它们实现自己处理接口或抽象类中定义的方法的方式。我试图弄清楚如果一个接口/抽象类是更好的或不。如果你只能使用带有虚拟抽象方法的抽象类,我就不明白接口的意义了。

下面是一个接口:

public interface IAthlete
{
    void Run();
}

下面是一个抽象类:

public abstract class Athlete
{
    public abstract void Run();
}

下面是接口的实现:

public class Sprinter : IAthlete
{
    public void Run()
    {
        Console.WriteLine("Running Fast....");
    }
}

下面是抽象类的扩展:

public class MarathonRunner : Athlete
{
    public override void Run()
    {
         Console.Write("Jogging....");
    }
 }

现在,如果我决定在接口或抽象方法中添加一个名为Stop的方法,Sprinter和MarathonRunner都会中断,因为我可以为抽象提供一些默认实现,这似乎是一个更好的选择。我错过什么了吗?

接口和抽象类只是具有虚拟抽象方法是一回事

接口和抽象超类有两个主要区别:

抽象类

  • 代码重用可以通过使用抽象超类
  • 实现
  • 你只能继承一个超类

接口
  • 每个方法必须在每个子类中实现
  • 一个类可以继承多个接口(多重继承)

如果您只有一个共性要提取,那么您非常正确地认为两者之间没有实质性的区别。但这就像说"在将1添加到2的情况下,intdouble之间没有区别"-这是技术上正确的,但不是一个特别有用的指导如何思考。

在比这更复杂的情况下(也就是说,在大多数情况下),将会有更多的类,以及需要提取的公共属性片段。然后,您必须开始在类继承和接口实现之间做出重要的选择,考虑如下事项:

  • 你只有一次选择基类的机会,但是你可以实现尽可能多的接口
  • 如果你想让你的'父'做任何工作,它需要是一个类而不是一个接口

等等

一般来说,类的语义应该指导你——当"事物"具有"IS - A"关系时(如MarathonRunner到Athlete),建议使用继承;当"事物"具有"我可以履行A的契约"(例如,Person to Runner)时,建议使用接口实现。

接口是一种更好的方法,因为目前。net开发者社区的共识是,你应该更喜欢组合而不是继承,所以接口是一种更好的策略(想想注入容器以及它们有多有用,让我们不要开始单元测试)。此外,类可以实现许多接口,但只能继承一个类(抽象或其他)。此外,结构可以实现接口,但不能从另一个类继承。在运行时级别,接口的效率更高,因为运行时不必遍历继承堆栈来计算调用特定成员的多态含义。

接口是一个非常有用的特性,与抽象类非常相似,在某些情况下,可以与抽象类交换。

但是,不要直接跳转到接口,除非必须这样做(Java开发人员中非常常见的反模式)。我建议,通过阅读你的例子,坚持使用抽象类。

大多数情况下,我只使用接口,当我有几个不相关的类,我需要他们有共同的成员,好像这些类来自同一个基类。

在你的例子中,你试图找到如果你需要一个新的stop方法,当添加一个基本的虚方法。这些问题可以用另一种方法解决,即不是抽象类vs 接口。

有三个选择:

(1)添加一个抽象方法,强制程序员重写它,以便实例化对象。

(2)添加一个新的虚拟方法,它可以做一些事情,但不需要被重写。

(3)添加一个不做任何事情的新方法,也许适用于你的情况。

// cannot instantiate an abstract class
public abstract class Athlete
{
    // helper method:
    public /* non-abstract */ void DoNothing()
    {
      // does nothing on purpouse !!!
    }
    // (1) virtual & abstract method, must be overriden
    public abstract void Run();

    // (2) new virtual method, doesn't need to be overriden,
    // but, maybe you dont like what it does
    public virtual void Stop()
    {
       Message.Show("Stop !!!");
    }
    // (3) new virtual method, doesn't need to be overriden,
    // its safe to be called
    public virtual void TakeBreak()
    {
      // works like an abstract virtual method, but, you don't need to override
      DoNothing();
    }
} // class Athlete
// in a non abstract class, you must override all abstract methods
public /* non-abstract */ class Runner: Athlete
{
    public override void Run()
    {
       DoNothing(); 
    }
    public override void Stop()
    {
       DoNothing(); 
    }
    // don't need to override this method
    // public virtual void TakeBreak();
} // class Trekker
// ...
Runner ARunner = new Runner();
ARunner.Run();
ARunner.Stop();
ARunner.TakeBreak();

第三种虚拟方法,可能适用于你的例子,没有一个特殊的名称,我已经在stackoverflow上发布了一个关于它的问题,但是,没有人知道这种情况下的特殊名称。

欢呼。

接口和抽象类之间的一个重要区别是它们的成员如何处理多代继承。假设有一个抽象类BaseFoo和抽象方法Bar,接口IFoo和方法Boz;类Foo继承BaseFoo并实现IFoo,类DerivedFoo继承Foo

如果DerivedFoo需要覆盖BaseFoo.Bar,它可以这样做,如果它需要使用父类的实现,它的覆盖可以调用base.Bar()。如果Foo使用虚方法隐式实现Boz,那么DerivedFoo可以覆盖该方法并调用base.Boz()(覆盖是类的函数而不是接口),但如果Foo显式实现IFoo.Boz,那么DerivedFoo改变IFoo.Boz行为的唯一方法将是重新实现它。如果这样做,那么FooIFoo.Boz的实现将变得不可访问,即使在DerivedFoo对相同接口成员的实现中也是如此。