36

在实现方面,我应该如何决定选择基本类型还是接口?我试图研究几个例子,但我没有得到完整的想法:(

关于如何和为什么的例子将不胜感激..

4

7 回答 7

49

基类,无论是否抽象,都可以包含已实现的成员。接口不能。如果您的所有实现都将执行类似的操作,那么基类可能是可行的方法,因为您的所有子类都可以共享基类上成员的相同实现。如果他们不打算共享实现,那么接口可能是要走的路。

例子:

class Person
{
    string Name { get; set; }
}

class Employee : Person
{
    string Company { get; set; }
}

Employee 从 Person 继承是有意义的,因为 Employee 类不必定义Name属性,因为它共享实现。

interface IPolygon
{
    double CalculateArea();
}

class Rectangle : IPolygon
{
    double Width { get; set; }
    double Height { get; set; }

    double CalculateArea()
    {
        return this.Width * this.Height;
    }
}

class Triangle : IPolygon
{
    double Base { get; set; }
    double Height { get; set; }

    double CalculateArea()
    {
        return 0.5 * this.Base * this.Height;
    }
}

因为RectangleTriangle具有如此不同的实现CalculateArea,所以它们从基类继承是没有意义的。

如果你做了一个基类,发现里面包含抽象成员,你还不如只用一个接口。

而且,正如 j__m 所说,您不能从多个基类继承,但可以实现多个接口。

我通常首先定义接口,如果我发现自己在我的实现中重复代码,我会创建一个实现接口的基类,并让我的实现从它继承。

于 2013-03-23T06:04:55.263 回答
21

要决定是使用抽象类还是接口,我觉得这篇文章很有帮助来源

对我来说,区分一个或另一个案例的一个好方法一直是:

  1. 是否有许多类可以“组合在一起”并由一个名词描述?如果是这样,则以该名词的名称创建一个抽象类,并从中继承类。(一个关键的决定因素是这些类共享功能,你永远不会只实例化一个Animal ......你总是会实例化某种Animal:你的Animal基类的实现) 示例CatDog都可以从抽象继承class Animal,这个抽象基类将实现一个方法void Breathe()因此,所有动物都会以完全相同的方式做这件事。(我可能会将此方法设为虚拟,以便我可以为某些动物覆盖它,例如 Fish,它的呼吸方式与大多数动物不同)。

  2. 什么样的动词可以应用于我的班级,通常也可以应用于其他人?为每个动词创建一个界面。 示例:所有动物都可以喂食,所以我将创建一个名为IFeedable的接口并让 Animal 实现它。只有DogHorse可以很好地实现ILikeable - 我不会在基类上实现它,因为这不适用于Cat

另请查看此接口与基类问题。

于 2013-03-23T06:35:27.170 回答
4

使用抽象类的原因之一是我们必须强制执行一些初始化(如通过构造函数的状态)。

接口不允许您定义构造函数的合同。

在下面的示例中,每个 Animal 对象都应该有一个 NAME。这不能通过接口强制执行。

public abstract class Animal
{
    public Animal(string name)
    {
        this.Name = name;
    }

    public string Name 
    { 
        get; 
        private set; 
    }
}

public class Cat : Animal
{
    public Cat(string name)
        : base(name)
    {

    }

    string NoOfLegs { get; set; }
}



class Program
{
    static void Main(string[] args)
    {
        Animal aCat = new Cat("a");
    }
}
于 2013-03-23T10:20:14.693 回答
3

实际上,它们不一定是相互排斥的。您可以根据代码实现的发展方式同时使用这两种方法。

接口通常是合同的表述。它定义了实现者应该遵守的预期行为。根据接口编写公共 api通常被认为是一种好习惯。这样可以减少与实现细节的耦合,并允许更轻松地重构和维护代码。现在,有资格称为公共 API 的是软件组件,它体现了您的设计中定义的高级交互,并且可以由您自己和其他人在同一项目中或在多个独立范围的项目中重用。 一个基类

已经是实施的一部分。是否实现接口。并且它将其潜在的层次结构与特定的实现细节联系起来:对象状态、可覆盖或不可覆盖的方法等。

于 2013-03-23T06:29:10.507 回答
2

接口是一种更灵活的构造。你只能有一个基类,但你可以实现许多接口。如果您需要一个对象来支持多种行为,但其中不止一种行为需要特定的基类,那么您将无法这样做。

正如 Dan 所指出的,基类在许多语言中具有方便的优势,因为您可以提供基本实现。要使用接口执行此操作,您需要创建一个提供基本实现的类,然后手动将每个接口方法的实现委托给该类 - 几乎没有那么方便。

于 2013-03-23T06:22:06.147 回答
1

我在这里找到一个类比有帮助:

抽象基类:- 汽车制造商可能会开发一种汽油发动机,它有一些变体(1.6、2L 等)。发动机缸体铸造可以被认为是抽象基类,即它定义了发动机的基本形状和特征。2L 版本可能有更大的气缸盖等。

接口:- 发动机也可以使用各种现成的组件,例如交流发电机、散热器、起动马达等,因此它必须实现由这些组件定义的接口。这些组件通常是在不了解可能使用它们的引擎的情况下设计的。

于 2015-12-16T11:25:55.933 回答
0

如果需要,您基本上可以同时使用两者。但通常基类也可以用于继承类中使用的某些方法或属性。如果您创建一个基类,最好将其抽象化;通过在基类中使用虚方法并在继承类中覆盖它并以不同的方式使用,您将更加灵活。

使用接口,您会更加灵活。首先,您在从此接口继承的类之间进行声明。有了这个

  • 你让你的代码更具可读性

  • 你把你的概念分开,比如从接口A继承的一个类可以连接Mssql数据库,而另一个可以连接Mysql数据库。

  • 你的代码变得更易于维护;Interfaces helps to reduce coupling and therefore allow you to easily interchange implementations for the same concept without the underlying code being affected

  • 您可以使用依赖注入

  • 您可以更轻松地创建单元测试。

    您可以在此处查看问题以获取更多信息。

于 2019-09-03T08:29:05.023 回答