繁体   English   中英

接口还是抽象类?

[英]Interface or abstract class?

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

我有两个类(简化):

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 - 但是这样做还有什么其他的IObject呢?

第二,如何在整个故事中添加IXmlSerializable 让接口继承IXmlSerializable还是在抽象基类中实现IXmlSerializable有更多的IXmlSerializable

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

你真的不能错过这种方法。 接口使您可以灵活地更改实现。 抽象类为您提供了不必使用的样板代码和帮助程序代码,如果您的方法是根据抽象类明确定义的,那么您将使用它们。

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

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

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

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

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

5A。 如果各种实现仅共享方法签名,那么最好使用接口。
5B。 如果各种实现具有相同的类型并使用共同的行为或状态,那么抽象类最好使用。

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

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

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

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

有理由使用基类之前,接口将是我的默认接口,因为它为我们做出的决策更少。

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

您的序列化要求到底是什么? 有可能是更好的选择。不过,对于许多串行一个基类比一个接口更容易。 例如,对于XmlSerializer您可以:

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

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

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

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

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

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

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

在上面的示例中,我将'MyObjectGroup'对象继承基础'MyObject'类,从这里我可以看到的接口无法获得。

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

  1. 对象的行为。
  2. 对象的实现。

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

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

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

1.使用抽象类进行业务建模

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

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

V 1.0(改变问题1)改变了问题陈述:你必须在1975年为印度铁路创建一个具有maxSpeed行为的Diesel Train

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

V 2.0(更改问题2)登上问题陈述:您必须为印度铁路创建一个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/

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

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

- >如果您希望您的子类在多个继承中使用接口实现多个不相关的功能。

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

抽象类:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM