接口还是抽象类?

时间:2009-07-22 13:40:15

标签: c# interface theory abstract

对于我的新宠物项目,我有一个设计问题,已经确定了,但我也想要其他一些意见。

我有两个类(简化):

class MyObject
{
  string name {get;set;}
  enum relation {get;set;}
  int value {get;set;}
}

class MyObjectGroup
{
  string name {get;set;}
  enum relation {get;set;}
  int value {get;set;}
  List<MyObject> myobjects {get;set;}
}

项目的后期MyObjectGroupMyObject应该平等使用。为此,我可以采取两种方式:

  • 创建界面:IObject
  • 创建一个抽象类:ObjectBase

我决定采用界面的方式,我以后在代码中不能每次都写ObjectBase,而IObject只是为了方便 - 但这样做的其他积极因素是什么?

第二,如何在整个故事中添加IXmlSerializable呢? 让接口继承自IXmlSerializable,还是在抽象基类中实现IXmlSerializable有更多积极因素?

15 个答案:

答案 0 :(得分:16)

一般来说,我在这种情况下使用的方法是同时拥有一个接口和一个抽象类。接口定义了接口。抽象类只是一个帮手。

这种方法真的不会出错。接口使您可以灵活地更改实现。抽象类为您提供了不被强制使用的样板和助手代码,如果您的方法是根据抽象类明确定义的,那么您将使用这些代码。

答案 1 :(得分:11)

这些是Interfaces和Abstract类之间的一些区别。

1A。类可以继承(实现)一个或多个接口。所以在C#中,接口用于实现多重继承。
1B。一个类只能继承一个抽象类。

2A。接口无法提供任何代码,只能提供签名。
2B。抽象类可以提供完整的默认代码和/或只需要覆盖的详细信息。

3A。接口不能具有子,函数,属性等的访问修饰符,所有内容都被视为公共。
3B。抽象类可以包含subs,functions,properties的访问修饰符。

4A。接口用于定义类的外围功能。例如。 ShipCar可以实现IMovable接口。
4B。抽象类定义了类的核心标识,并在那里用于对象。

5A。如果各种实现只共享方法签名,那么最好使用Interfaces。
5B。如果各种实现属于同一类并且使用常见行为或状态,那么抽象类最好使用。

6A。如果我们向接口添加新方法,那么我们必须跟踪接口的所有实现并定义新方法的实现。
6B。如果我们向抽象类添加一个新方法,那么我们可以选择提供默认实现,因此所有现有代码都可以正常工作。

7A。接口不能定义字段。
7B。抽象类可以定义字段和常量。

8A。接口不能有构造函数。
8B。抽象类可以实现默认构造函数。

9A。接口只能从其他接口继承。
9B。抽象类可以从接口,抽象类甚至类继承。

答案 2 :(得分:7)

原因使用基类之前,接口将是我的默认值,因为它为我们做出的决策较少。

除非我必须这样做,否则我不会涉及IXmlSerializable;它是一个混乱,棘手的界面,往往是一个祸患。

您的序列化要求到底是什么?可能有更好的选择......但是,对于许多序列化程序而言,基类比接口更容易。例如,对于XmlSerializer,您可以:

[XmlInclude(typeof(MyObject))] // : ObjectBase 
[XmlInclude(typeof(MyObjectGroup))] // : ObjectBase 
public abstract class ObjectBase { /*  */ }

(确切的方法取决于序列化器)

答案 3 :(得分:5)

通常,您应该将接口视为 contract ,某些类型实现并将抽象类视为继承层次结构中的节点,这些节点本身并不存在(即存在< em>“是派生类和基本抽象类之间的”关系。但是,在实践中,您可能需要在其他情况下使用接口,例如需要多重继承时。

例如,IXmlSerializable本身不是“实体”。它定义了实体可以实现的合同。接口位于继承层次结构的“外部”。

答案 4 :(得分:4)

接口允许您通过提供接口所描述的属性和方法来定义对象需要实现的“合同”。您可以通过interface-type的变量引用对象,这可能会导致对提供的内容产生一些混淆。

基类提供了构建继承“树”的机会,其中更复杂的类(常见的“类型”)构建在更简单的“基础”类的基础上。 OO中经典又烦人的例子通常是'Shape'的基类,由Triangle,Square等继承。

重点是,使用接口,您需要为实现它的每个类提供整个合同,使用继承树(基类),您只需更改/添加子类唯一的属性和方法,公共属性和方法保留在基类中。

在上面的例子中,我的'MyObjectGroup'对象继承了基础'MyObject'类,没有什么可以从我可以看到的界面中获得。

答案 5 :(得分:3)

在设计课程时,建筑师有两件事情。

  1. 对象的行为。
  2. 对象的实现。
  3. 如果一个实体有多个实现,那么将一个对象的行为与其实现分开是可维护性和解耦的关键之一。 可以通过抽象类或接口实现分离,但哪一个最好?让我们举一个例子来检查一下。

    让我们看一下开发场景,其中事物(请求,类模型等)经常发生变化,您必须提供某些版本的应用程序。

    最初的问题陈述:你必须为1970年为maxSpeed行为的印度铁路创建一个“火车”课程。

    1。使用抽象类

    进行业务建模

    V 0.0(初始问题) 最初的问题陈述:你必须为印度铁路创建一个Train类,它在1970年具有maxSpeed的行为。

    public abstract class Train {
        public int maxSpeed();
    }
    

    V 1.0(已更改问题1) 改变了问题陈述:您必须在1975年为印度铁路创建一个Diesel Train类,其行为为maxSpeed。

    public abstract class DieselTrain extends train {
         public int maxFuelCapacity ();
    }
    

    V 2.0(已更改问题2) chanded问题陈述:你必须为印度铁路创建一个ElectricalTrain类,它在1980年具有maxSpeed,maxVoltage的行为。

    public abstract class ElectricalTrain extends train {
         public int maxvoltage ();
    }
    

    V 3.0(已更改问题3)

    chanded问题陈述:您必须为印度铁路创建一个HybridTrain(同时使用柴油和电力)类,它在1985年具有maxSpeed,maxVoltage,maxVoltage的行为。

    public abstract class HybridTrain extends ElectricalTrain , DisealTrain {
        { Not possible in java }
    }
    {here Business modeling with abstract class fails}
    

    2。带界面的业务建模

    只需将abstract字改为interface并...... 您的业​​务建模与界面将成功。

    http://javaqna.wordpress.com/2008/08/24/why-the-use-on-interfaces-instead-of-abstract-classes-is-encouraged-in-java-programming/

答案 6 :(得分:2)

<强>接口: 如果您的子类都应该实现某组方法/功能,但每个子类都可以自由提供它自己的实现,那么使用接口。

例如如果要为车辆实现类层次结构,请实现名为Vehicle的接口,该接口具有Color MaxSpeed等属性和Drive()等方法。所有儿童类如Car Scooter AirPlane SolarCar等都应该来自这个基础界面,但是可以单独实现车辆暴露的方法和属性。

- &GT;如果您希望您的子类在短多重继承中使用接口来实现多个不相关的功能。

例如如果你正在实现一个名为SpaceShip的类,它必须具有来自Vehicle和UFO的功能,那么将Vehicle和UFO都作为接口,然后创建一个实现Vehicle和UFO的SpaceShip类。

抽象类:

- &GT;当你有一个要求,你的基类应该提供某些方法的默认实现,而其他方法应该被子类重写,使用抽象类。

例如再举一个上面的Vehicle类的例子。如果我们希望从Vehicle派生的所有类以固定方式实现Drive()方法,而其他方法可以被子类重写。在这种情况下,我们将Vehicle类实现为具有Drive实现的抽象类,同时将其他方法/属性保留为抽象,以便它们可以被子类重写。

- &GT;抽象类的目的是提供多个派生类可以共享的基类的通用定义。

例如,类库可以定义一个抽象类,该抽象类用作其许多函数的参数,并要求程序员使用该库通过创建派生类来提供自己的类实现。

答案 7 :(得分:1)

你实际上可以和BOTH一起去。 ObjectBase为您省去了多次实现公共属性的麻烦,并为您实现了IObject。您使用它的任何地方都会引用IObject,因此您可以稍后使用模拟进行测试

答案 8 :(得分:1)

我宁愿选择基础抽象类,因为理论上(好吧,它只是一种理论,我没有证明或说任何其他更糟糕的事情) - 当你想展示时应该使用接口,某些对象能够做某事(比如IComparable - 你表明无论什么实现它,都可以与其他东西进行比较),而当你有2个实例只是共享一些共同点或者有1个逻辑父类时 - 应该使用抽象类。
你也可以使用基类来实现这两种方法,它将实现一个接口,它将明确指出你的类可以做什么。

答案 9 :(得分:1)

其他条件相同,请使用界面。更容易模拟单元测试。

但一般来说,我所有使用基类的是当我有一些常见的代码放在一个地方时,而不是派生类的每个实例。如果它与你所描述的类似,它们的使用方式是相同的,但它们的底层机制是不同的,界面听起来更合适。

答案 10 :(得分:0)

我一直在我的项目中使用抽象类,但在将来的项目中,我将使用接口。 “多重继承”的优点非常有用。 无论是在代码中还是为了测试目的,都能够提供全新的类实现,我们总是受欢迎。 最后,如果您将来希望能够由外部开发人员自定义代码,则无需向他们提供实际代码 - 他们只需使用接口即可...

答案 11 :(得分:0)

如果你在类中有函数,你应该使用抽象类而不是接口。 通常,接口用于代表类型。

答案 12 :(得分:0)

请注意,您无法覆盖Interfaces中的运算符。就我而言,这是他们唯一真正的问题。

答案 13 :(得分:0)

选择接口和抽象类不是一个/或命题。如果您需要更改设计,请将其设为界面。但是,您可能具有提供某些默认行为的抽象类。抽象类是应用程序框架内的优秀候选者。

抽象类可以让你定义一些行为;他们强迫你的子类提供其他人。例如,如果您有应用程序框架,则抽象类可以提供默认服务,例如事件和消息处理。这些服务允许您的应用程序插入您的应用程序框架。但是,有一些特定于应用程序的功能,只有您的应用程序才能执行。此类功能可能包括启动和关闭任务,这些任务通常取决于应用程序。因此,抽象基类可以声明抽象关闭和启动方法,而不是尝试自己定义该行为。基类知道它需要那些方法,但是抽象类允许你的类承认它不知道如何执行这些操作;它只知道它必须启动行动。在启动时,抽象类可以调用启动方法。当基类调用此方法时,Java会调用子类定义的方法。

许多开发人员忘记了定义抽象方法的类也可以调用该方法。抽象类是创建计划继承层次结构的绝佳方法。对于类层次结构中的非叶类,它们也是一个很好的选择。

答案 14 :(得分:0)

抽象类的定义可能描述代码和状态,从它们派生的类可能不会同时从其他类派生。这就是技术上的差异。

因此,从使用和使用的角度来看哲学,不同之处在于,通过设置抽象类,您可以约束该类的对象可以实现的任何其他功能,并为这些对象提供任何此类对象常见的一些基本功能(这是一种约束,同时,通过设置界面,您不会为其他功能设置任何约束,也不会为您考虑的功能制定实际代码规定。当你知道这个类的对象应该为了用户的利益而应该做的所有事情时,使用抽象类。当对象也可以做其他你现在甚至无法猜到的事情时,使用接口。