关于C#:接口或抽象类?

Interface or abstract class?

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

我有两个课程(简化):

1
2
3
4
5
6
7
8
9
10
11
12
13
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;}
}

在项目后期,MyObjectGroupMyObject应被同等使用。为此,我可以采取两种方式:

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

我决定采用接口的方式,稍后在代码中我不能每次都写ObjectBase,但是为了方便起见,IObject除外——但是这种方式还有什么好处呢?

第二,在整个故事中加入IXmlSerializable怎么样?让接口继承IXmlSerializable,或者在抽象基类中实现IXmlSerializable是否更积极?


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

这种方法你真的不会出错。接口为您提供了更改实现的灵活性。抽象类为您提供了不必强制使用的样板和帮助程序代码,否则,如果您的方法是用抽象类显式定义的,那么您将使用样板和帮助程序代码。


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

一个类可以继承(实现)一个或多个接口。因此在C中,接口被用来实现多重继承。< BR>一个类只能继承一个抽象类。

接口不能提供任何代码,只能提供签名。< BR>抽象类可以提供完整的默认代码和/或只提供必须重写的细节。

3a.接口不能有子、函数、属性等的访问修饰符。一切都假定为公共的。< BR>抽象类可以包含子类、函数和属性的访问修饰符。

接口用于定义类的外围能力。例如,ShipCar可以实现IMovable接口。< BR>一个抽象类定义了一个类的核心标识,并在那里用于对象。

如果各种实现只共享方法签名,那么最好使用接口。< BR>如果不同的实现类型相同,并且使用公共行为或状态,那么抽象类更适合使用。

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

7a.接口不能定义字段。< BR>抽象类可以定义字段和常量。

8a.接口不能有构造函数。< BR>抽象类可以实现默认的构造函数。

9a.接口只能从其他接口继承。< BR>9B.抽象类可以从接口、抽象类甚至类继承。


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

我不会让IXmlSerializable参与进来,除非我不得不这样做;这是一个混乱的、棘手的界面,常常是造成灾难的原因。

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

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

(具体方法取决于序列化程序)


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

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


接口将允许您定义一个"契约",对象将需要通过交付接口所描述的属性和方法来实现该契约。您可以通过接口类型的变量来引用对象,这可能会导致对提供的确切内容产生一些混淆。

基类提供了构建继承"树"的机会,其中更复杂的类(公共的"类型")构建在更简单的"基类"的基础上。OO中经典而烦人的例子通常是一个"形状"的基类,由三角形、正方形等继承。

主要的一点是,对于一个接口,您需要为实现它的每个类提供整个契约,对于继承树(基类),您只需要更改/添加子类特有的属性和方法,公共属性和方法仍保留在基类中。

在上面的示例中,我将让"myObjectGroup"对象继承基本的"myObject"类,在这里我可以看到,从接口中什么也得不到。


在设计类时,架构师有两个想法。

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

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

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

    1。抽象类业务建模

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

    1
    2
    3
    public abstract class Train {
        public int maxSpeed();
    }

    V 1.0(更改问题1)改变了问题陈述:你必须为印度铁路创建一个Diesel Train类,该类铁路在1975年的运行方式为maxspeed。

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

    V 2.0(更改问题2)Chanded问题陈述:你必须为印度铁路创建一个ElectricalTrain类,该类铁路在1980年的行为为Maxspeed,MaxVoltage。

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

    V 3.0(更改问题3)

    钱德问题陈述:你必须为印度铁路创建一个HybridTrain类(柴油和电力都使用),该类铁路在1985年的行为为MaxSpeed、MaxVoltage和MaxVoltage。

    1
    2
    3
    4
    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-welled-in-java-programming/


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

    例如,如果要为车辆实现类层次结构,请实现名为vehicle的接口,该接口具有诸如colour maxspeed等属性和drive()等方法。所有的子类,如汽车滑板车飞机SOLARCAR等,都应该从这个基本接口派生出来,但是提供了一个单独的方法和车辆所暴露的属性的实现。

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

    例如,如果你正在实现一个名为"宇宙飞船"的类,该类必须具有来自车辆和UFO的功能,那么将车辆和UFO作为接口,然后创建一个实现车辆和UFO的类宇宙飞船。

    抽象类:

    –>当您有一个要求时,您的基类应该提供某些方法的默认实现,而其他方法应该可以被子类覆盖,使用抽象类。

    例如,再次以上述车辆类别为例。如果我们希望从vehicle派生的所有类以固定方式实现drive()方法,而其他方法可以被子类重写。在这种情况下,我们将vehicle类实现为一个带有drive实现的抽象类,而将其他方法/属性保留为抽象类,以便它们可以被子类覆盖。

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

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


    所有其他条件都一样,使用接口。更容易模拟单元测试。

    但是,一般来说,我使用基类的目的是当有一些公共代码我宁愿放在一个地方,而不是派生类的每个实例时。如果是像你描述的那样的东西,它们的使用方式是相同的,但是它们的底层机制是不同的,那么界面听起来更合适。


    我宁愿选择基本抽象类,因为,理论上(好吧,这只是一个理论,我不是在证明或说任何其他的比这更糟),当你想证明某个对象能够做某些事情(比如,可比较的-你表明无论实现什么,都可以与其他东西相比),当EAS当您有两个实例共享公共内容或有一个逻辑父类时,应该使用抽象类。您也可以使用两种方法,使用基类来实现一个接口,它将显式地指出类可以做什么。


    你可以两者兼得。ObjectBase为您省去了多次实现公共属性的麻烦,并为您实现了IObject。无论在何处使用它,都会引用iobject,以便稍后使用mock进行测试。


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

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


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

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

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


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


    如果您在类中有函数,那么应该使用Abstract类而不是Interface。通常,接口用于代表类型。


    我已经在我的项目中使用了抽象类,但在未来的项目中,我将使用接口。"多重继承"的优点非常有用。无论是在代码中还是出于测试目的,都可以提供类的全新实现,这一点一直受到欢迎。最后,如果将来你想让外部开发人员定制你的代码,你不需要给他们你真正的代码-他们只需要使用接口…