14

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

我有两个类(简化):

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;}
}

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

  • 创建接口:IObject
  • 创建一个抽象类:ObjectBase

我决定走接口的方式,我以后在代码中不能ObjectBase每次都写,而IObject只是为了方便 - 但是这种方式还有什么其他好处?

其次,添加IXmlSerializable到整个故事中怎么样?让接口继承自抽象基类IXmlSerializable还是有更多优点可以IXmlSerializable在抽象基类中实现?

4

15 回答 15

16

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

这种方法真的不会出错。接口使您可以灵活地更改实现。抽象类为您提供不必强制使用的样板代码和帮助程序代码,否则如果您的方法是根据抽象类明确定义的,您就会使用这些代码。

于 2009-07-22T13:45:03.860 回答
15

这些是接口和抽象类之间的一些区别。

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

2A。接口不能提供任何代码,只能提供签名。
2B。抽象类可以提供完整的默认代码和/或仅提供必须覆盖的细节。

3A。一个接口不能有子、函数、属性等的访问修饰符,一切都被假定为公共的。
3B。抽象类可以包含子类、函数、属性的访问修饰符。

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

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

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

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

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

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

于 2014-05-07T10:35:46.690 回答
6

在有理由使用基类之前,该接口将是我的默认设置,因为它为我们做的决定更少。

IXmlSerializable除非我不得不这样做,否则我不会参与;它是一个混乱、棘手的界面,通常是造成麻烦的原因。

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

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

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

于 2009-07-22T13:45:23.657 回答
5

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

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

于 2009-07-22T13:45:13.830 回答
4

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

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

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

在您上面的示例中,我将让“MyObjectGroup”对象继承基“MyObject”类,从我可以看到的接口中没有任何收获。

于 2009-07-22T13:50:02.183 回答
3

在设计课程时,建筑师的脑海中有两件事。

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

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

让我们来看一个开发场景,其中事物(请求、类模型等)变化非常频繁,您必须交付某些版本的应用程序。

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

1. 抽象类的业务建模

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

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

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

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)

问题陈述:您必须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/

于 2011-02-23T18:14:04.593 回答
2

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

例如,如果您正在为车辆实现一个类层次结构,请实现一个名为 Vehicle 的接口,该接口具有 Color MaxSpeed 等属性和 Drive() 等方法。所有子类,如 Car Scooter AirPlane SolarCar 等都应该从这个基接口派生,但提供由 Vehicle 公开的方法和属性的单独实现。

–> 如果您希望您的子类在简短的多重继承中实现多个不相关的功能,请使用接口。

例如,如果您正在实现一个名为 SpaceShip 的类,该类必须具有 Vehicle 和 UFO 的功能,然后将 Vehicle 和 UFO 作为接口,然后创建一个实现 Vehicle 和 UFO 的类 SpaceShip。

抽象类:

–> 当您的基类应提供某些方法的默认实现而其他方法应开放以被子类覆盖时,请使用抽象类。

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

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

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

于 2016-06-19T06:13:50.493 回答
1

你实际上可以和两者一起去。ObjectBase 为您省去了多次实现常用属性的麻烦,并为您实现了 IObject。在你使用它的任何地方都引用 IObject,这样你以后就可以使用 mocks 进行测试

于 2009-07-22T13:45:15.310 回答
1

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

于 2009-07-22T13:50:12.467 回答
1

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

于 2009-07-22T13:56:49.353 回答
1

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

但一般来说,我使用基类的只是当有一些公共代码我宁愿放在一个地方,而不是派生类的每个实例时。如果是用于您所描述的内容,它们的使用方式相同,但它们的基本机制不同,则界面听起来更合适。

于 2009-07-22T14:23:36.630 回答
0

我一直在我的项目中使用抽象类,但在未来的项目中,我将使用接口。“多重继承”的优势非常有用。无论是在代码中还是出于测试目的,都欢迎能够提供该类的全新实现。最后,如果将来您希望能够由外部开发人员自定义您的代码,您不必向他们提供您的真实代码——他们只需使用接口...

于 2009-07-22T13:44:50.757 回答
0

如果你在类中有函数,你应该使用抽象类而不是接口。一般来说,一个接口用来代表一种类型。

于 2009-07-22T13:53:55.783 回答
0

选择接口和抽象类不是一个非此即彼的命题。如果你需要改变你的设计,把它变成一个界面。但是,您可能有提供一些默认行为的抽象类。抽象类是应用程序框架内的优秀候选者。

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

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

于 2013-12-20T18:05:35.117 回答
0

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

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

于 2016-03-30T13:47:00.727 回答