这可能是一个通用的OOP问题。我想在接口和抽象类的使用基础上做一个通用的比较。

什么时候需要使用接口,什么时候需要使用抽象类?


当前回答

我写过一篇关于何时使用抽象类和何时使用接口的文章。除了“一个是a……”之外,他们之间还有很多不同之处。一个能做……”。对我来说,这些都是事先准备好的答案。我提到了使用它们中的任何一种的一些原因。希望能有所帮助。

http://codeofdoom.com/wordpress/2009/02/12/learn-this-when-to-use-an-abstract-class-and-an-interface/

其他回答

我认为最简洁的说法是:

共享属性=>抽象类。 共享功能=>接口。

更简单地说……

抽象类示例:

public abstract class BaseAnimal
{
    public int NumberOfLegs { get; set; }

    protected BaseAnimal(int numberOfLegs)
    {
        NumberOfLegs = numberOfLegs;
    }
}

public class Dog : BaseAnimal
{
    public Dog() : base(4) { }
}

public class Human : BaseAnimal 
{
    public Human() : base(2) { }
}

由于动物有一个共同的属性——在这种情况下是腿的数量——创建一个包含这个共同属性的抽象类是有意义的。这也允许我们编写操作该属性的通用代码。例如:

public static int CountAllLegs(List<BaseAnimal> animals)
{
    int legCount = 0;
    foreach (BaseAnimal animal in animals)
    {
        legCount += animal.NumberOfLegs;
    }
    return legCount;
}

接口的例子:

public interface IMakeSound
{
    void MakeSound();
}

public class Car : IMakeSound
{
    public void MakeSound() => Console.WriteLine("Vroom!");
}

public class Vuvuzela : IMakeSound
{
    public void MakeSound() => Console.WriteLine("VZZZZZZZZZZZZZ!");        
}

这里要注意的是,呜呜祖拉和汽车是完全不同的东西,但它们有共同的功能:发出声音。因此,接口在这里是有意义的。此外,它将允许程序员将发出声音的东西分组在一个公共界面下——在本例中是IMakeSound。通过这种设计,你可以编写以下代码:

List<IMakeSound> soundMakers = new List<ImakeSound>();
soundMakers.Add(new Car());
soundMakers.Add(new Vuvuzela());
soundMakers.Add(new Car());
soundMakers.Add(new Vuvuzela());
soundMakers.Add(new Vuvuzela());

foreach (IMakeSound soundMaker in soundMakers)
{
    soundMaker.MakeSound();
}

你知道那会输出什么吗?

最后,您可以将两者结合起来。

结合例子:

public interface IMakeSound
{
    void MakeSound();
}

public abstract class BaseAnimal : IMakeSound
{
    public int NumberOfLegs { get; set; }

    protected BaseAnimal(int numberOfLegs)
    {
        NumberOfLegs = numberOfLegs;
    }

    public abstract void MakeSound();
}

public class Cat : BaseAnimal
{
    public Cat() : base(4) { }

    public override void MakeSound() => Console.WriteLine("Meow!");
}

public class Human : BaseAnimal 
{
    public Human() : base(2) { }

    public override void MakeSound() => Console.WriteLine("Hello, world!");
}

这里,我们要求所有的BaseAnimals发出声音,但是我们还不知道它的实现。在这种情况下,我们可以抽象接口实现并将其实现委托给它的子类。

最后一点,还记得在抽象类示例中我们如何操作不同对象的共享属性,以及在接口示例中我们如何调用不同对象的共享功能吗?在最后一个例子中,我们可以两者都做。

抽象类可以有实现。

接口没有实现,它只是定义了一种契约。

也可能存在一些依赖于语言的差异:例如c#没有多重继承,但在一个类中可以实现多个接口。

这是一个很难打的电话。

我可以给出一个提示:一个对象可以实现许多接口,而一个对象只能继承一个基类(在像c#这样的现代OO语言中,我知道c++有多个继承-但这不是不受欢迎吗?)

两者都是类定义的契约:

结论1:两种意图都是对象泛化

在定义抽象类时,它们也可以有默认实现。

结论2:区分存在于行为泛化设计中

在使用抽象类时,类只能从一个抽象类继承

结论3:抽象类在应用上存在局限性。它的意思是 行为概括的局限性。

最后结论-何时使用哪个:区分是在行为泛化层面

在类的行为设计中,如果功能在确定的类之间只是概念上的限制,换句话说,在确定的类之间是共享的,则使用抽象类。但如果功能比确定类更通用,或者我们可以/想要向其他类添加功能,则使用接口作为契约。

好吧,我自己刚刚“grokup”了这个——这是外行的术语(如果我错了,请随意纠正我)——我知道这个话题太老了,但其他人可能有一天会偶然发现它……

抽象类允许你创建一个蓝图,并允许你额外构造(实现)属性和方法,你希望它的所有后代都拥有。

另一方面,接口只允许您声明希望在实现它的所有类中存在具有给定名称的属性和/或方法——但不指定应该如何实现它。同样,一个类可以实现许多接口,但只能扩展一个抽象类。界面更像是一种高级架构工具(如果你开始掌握设计模式,这一点就会变得更清楚)——抽象则同时涉足这两个阵营,也可以执行一些繁琐的工作。

为什么使用其中一种而不是另一种呢?前者允许对后代进行更具体的定义,而后者允许更大的多态性。最后一点对最终用户/编码器很重要,他们可以利用这些信息以各种组合/形状来实现A.P.I(界面),以满足他们的需求。

我认为这对我来说是一个“顿悟”的时刻——少从作者的角度来考虑接口,多从后面加入到项目中实现或扩展API的编码器的角度来考虑接口。