界面还是抽象类?
对于我的新的宠物项目,我有一个devise问题,这已经决定了,但我也想要一些其他的意见。
我有两个类(简体):
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
应平等使用。 为此,我可以采取两种方式:
- 创build一个接口:
IObject
- 创build一个抽象类:
ObjectBase
我决定去接口的方式,我以后在代码中不能每次都写ObjectBase
而只是为了方便而使用IObject
,但是这样做还有什么其他的IObject
呢?
其次,如何在整个故事中添加IXmlSerializable
? 让接口从IXmlSerializable
inheritance,还是在抽象基类中实现IXmlSerializable
有更多的IXmlSerializable
?
一般来说,我在这种情况下使用的方法是同时拥有一个接口和一个抽象类。 接口定义了接口。 抽象类只是一个帮手。
你真的不能用这种方法出错。 接口为您提供了更改实施的灵活性。 抽象类为您提供了样板和帮助程序代码,您不必强制使用这些代码,否则,如果您的方法是根据抽象类明确定义的,那么您将使用这些代码。
接口将是我的默认值,直到有一个使用基类的理由 ,因为它减less了我们的决定。
我不会涉及IXmlSerializable
除非我必须; 这是一个混乱,棘手的界面,往往是一个悲哀的原因。
你的序列化要求是什么? 可能有更好的select…但是,对于许多序列化器来说,基类比接口更容易。 例如,对于XmlSerializer
你可以有:
[XmlInclude(typeof(MyObject))] // : ObjectBase [XmlInclude(typeof(MyObjectGroup))] // : ObjectBase public abstract class ObjectBase { /* */ }
(确切的方法取决于序列化程序)
这些是Interfaces和Abstract类之间的一些区别。
1A。 一个类可以inheritance(实现)一个或多个接口。 所以在C#中,接口被用来实现多重inheritance。
1B。 一个类可能只inheritance一个抽象类。
2A。 一个接口不能提供任何代码,只是签名。
2B。 抽象类可以提供完整的默认代码和/或仅仅是必须被覆盖的细节。
3A。 一个接口不能具有访问修饰符的子,function,属性等一切都假定为公共。
3B。 抽象类可以包含对subs,functions,properties的访问修饰符。
4A。 接口用来定义一个类的外围能力。 例如。 Ship
和Car
可以实现一个IMovable
接口。
4B。 一个抽象类定义了一个类的核心标识,并在那里用于对象。
5A。 如果各种实现仅共享方法签名,则最好使用接口。
5B。 如果各种实现是相同types的并且使用普通的行为或状态,那么抽象类更好用。
6A。 如果我们为接口添加一个新的方法,那么我们必须跟踪接口的所有实现,并定义新方法的实现。
6B。 如果我们为抽象类添加一个新方法,那么我们可以select提供默认实现,因此所有现有的代码都可以正常工作。
7A。 一个接口不能有定义的字段。
7B。 抽象类可以定义字段和常量。
8A。 一个接口不能有构造函数。
8B。 抽象类可以实现默认的构造函数。
9A。 一个接口只能从其他接口inheritance。
9B。 抽象类可以inheritance接口,抽象类,甚至类。
一般来说,你应该把接口视为某些types实现的契约 ,并将类抽象为inheritance层次结构中不存在的节点 (即派生类和基本抽象类之间存在“是”关系)。 但是,在实际中,您可能需要在其他情况下使用接口,例如在需要多重inheritance时。
例如, IXmlSerializable
本身不是一个“实体”。 它定义了一个实体可以执行的合同。 接口居住在inheritance层次之外。
一个接口将允许你定义一个对象需要通过交付接口描述的属性和方法来实现的“契约”。 您可以通过接口types的variables来引用对象,这可能会导致一些混淆,究竟是在提供什么。
基类提供了构buildinheritance“树”的机会,更简单的“基”类的基础上build立了更复杂的类(通用“types”)。 OO中的经典和令人讨厌的例子通常是“形状”的基类,并由三angular形,方形等inheritance。
主要的一点是,通过一个接口,你需要为每一个实现它的类提供整个契约,用一个inheritance树(基类),你只需要改变/添加子类独有的属性和方法,通用属性方法仍然在基类中。
在你上面的例子中,我会让'MyObjectGroup'对象inheritance基类'MyObject'类,在这里我可以看到没有任何东西可以从接口获得。
在devise课程时,build筑师心中有两件事。
- 对象的行为。
- 对象的实现。
如果一个实体具有多个实现,那么将对象的行为与其实现分开是可维护性和解耦的关键之一。 抽象类或接口可以实现分离,但哪一个最好? 让我们举个例子来检查一下。
让我们来看看事情(请求,类模型等)变化频繁的开发场景,你必须提供某些版本的应用程序。
最初的问题陈述 :你必须为1970年有maxSpeed行为的印度铁路创build一个“列车”class。
1.业务build模与抽象类
V 0.0(初始问题)最初的问题陈述:你必须为1970年的maxSpeed行为的印度铁路创build一个Train
类。
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)着陆问题陈述:必须为1980年的maxSpeed,maxVoltage行为的印度铁路创build一个ElectricalTrain
类。
public abstract class ElectricalTrain extends train { public int maxvoltage (); }
V 3.0(更改问题3)
着陆问题陈述:你必须为印度铁路创build一个HybridTrain
(同时使用柴油和电力)类,其中1985年有maxSpeed,maxVoltage,maxVoltage行为。
public abstract class HybridTrain extends ElectricalTrain , DisealTrain { { Not possible in java } } {here Business modeling with abstract class fails}
2.业务build模与接口
只需将abstract
词更改为interface
……您的业务build模与界面将成功。
接口:如果你的子类应该都实现了一组方法/function,但是每个子类都可以自由地提供它自己的实现,那么就使用接口。
例如,如果您正在为车辆实现类层次结构,则实现一个名为Vehicle的接口,该接口具有Color MaxSpeed等的属性以及Drive()等方法。 所有的儿童类,如小型摩托车AirPlane SolarCar等,应该从这个基础接口派生,但提供了车辆的方法和属性的单独实施。
– >如果你想让你的子类在多重inheritance使用接口中实现多个不相关的function。
例如,如果您正在实施一个名为SpaceShip的类,该类必须具有车辆和UFO中的function,则将Vehicle和UFO作为接口,然后创build一个实现Vehicle和UFO的Class SpaceShip。
抽象类:
– >当你有一个要求,你的基类应该提供某些方法的默认实现,而其他的方法应该被打开,被子类覆盖,使用抽象类。
例如再次以上面的Vehicle类为例。 如果我们希望从Vehicle派生的所有类以固定的方式实现Drive()方法,而其他方法可以被子类覆盖。 在这种情况下,我们将Vehicle类作为具有Drive实现的抽象类来实现,而将其他方法/属性保留为抽象类,以便它们可以被子类覆盖。
– >抽象类的目的是提供多个派生类可以共享的基类的通用定义。
例如,一个类库可能会定义一个抽象类,它被用作许多函数的参数,并要求程序员使用该类库来创build派生类来提供它们自己的类实现。
你实际上可以一起去。 ObjectBase为您节省了多次执行公共属性的麻烦,并为您实现了IObject。 在任何你使用它的地方都可以参考IObject,所以你可以在以后使用mocks来testing
我宁愿去基础抽象类,因为理论上(这只是一个理论,我没有certificate或者说任何其他的比这更糟糕) – 当你想要展示的时候,应该使用接口,对象有能力做某事(比如IComparable – 你会发现无论是否实现它,都可以与其他东西进行比较),而当你有两个实例共享一些共同的东西或者有一个逻辑父 – 应该使用抽象类。
你也可以使用两种方法,使用基类,它将实现一个接口,它将明确指出你的类可以做什么。
一切都是平等的,去接口。 更容易模拟出unit testing。
但是一般来说,我使用的所有基类都是当我想把一些通用的代码放在一个地方,而不是每个派生类的实例时。 如果是像你所描述的那样,他们使用的方式是一样的,但是他们的底层机制是不同的,一个界面听起来更合适。
我一直在我的项目中使用抽象类,但在未来的项目中,我将使用接口。 “多重inheritance”的好处是非常有用的。 总是欢迎有能力提供一个全新的类实现,无论是在代码中,还是用于testing目的。 最后,如果将来您希望能够通过外部开发人员自定义您的代码,则不必给他们提供真正的代码 – 他们只需使用接口即可。
如果你在类中有函数,你应该使用抽象类而不是接口。 一般来说,一个接口被用来代表一个types。
请注意,您不能覆盖Interfaces中的运算符。 就我而言,这是他们唯一真正的问题。
select接口和抽象类不是一个或两个命题。 如果您需要更改devise,请将其设为界面。 但是,您可能具有提供某些默认行为的抽象类。 抽象类是应用程序框架内的优秀候选者。
抽象类让你定义一些行为; 他们强迫你的子类提供其他人。 例如,如果您有一个应用程序框架,抽象类可以提供默认服务,如事件和消息处理。 这些服务允许您的应用程序插入到您的应用程序框架。 但是,只有您的应用程序可以执行一些特定于应用程序的function。 这些function可能包括启动和closures任务,这些任务通常取决于应用程序。 因此,抽象基类可以声明抽象的closures和启动方法,而不是试图自己定义这个行为。 基类知道它需要这些方法,但抽象类让你的类承认它不知道如何执行这些操作; 它只知道它必须发起行动。 当启动的时候,抽象类可以调用启动方法。 当基类调用这个方法时,Java调用由子类定义的方法。
许多开发人员忘记了定义抽象方法的类也可以调用该方法。 抽象类是创build计划inheritance层次结构的绝佳方式。 对于类层次结构中的非叶类,它们也是一个很好的select。
抽象类的定义可能会描述代码和状态,从它们派生的类可能不会同时从其他类派生。 这就是技术的不同之处。
因此,从使用和哲学的angular度来看,不同之处在于,通过设置一个抽象类,可以约束该类的对象可能实现的任何其他function,并为这些对象提供一些常见的基本function这样的对象(这也是一种约束),而通过build立一个接口,你没有为其他function设置约束,也没有为你想到的那个function做任何实际的规定。 当你了解这个类的对象为了他们的用户的利益所应该做的一切时,使用抽象类。 当对象也可以做别的事情的时候使用接口,现在你甚至不能猜测。