何时应该使用接口,何时应该使用基类? 算法
若是我不想实际定义方法的基本实现,是否应该始终是一个接口? 编程
若是我有猫狗班。 为何我要实现IPet而不是PetBase? 我能够理解具备用于ISheds或IBarks(IMakesNoise?)的接口,由于能够将它们逐个放置在每一个宠物上,可是我不知道用于普通Pet的接口。 框架
我有一个粗略的经验法则 编码
功能:各个部分可能有所不一样:接口。 spa
数据和功能,部分将大部分相同,而部分则不一样:抽象类。 插件
数据和功能,若是仅进行了少许更改就能够实际使用:普通(具体)类 设计
数据和功能,没有计划的更改:带有final修饰符的普通(具体)类。 指针
数据,可能还有功能:只读:枚举成员。 code
这是很是粗糙且准备就绪的,而且根本没有严格定义,可是从接口的频谱能够将全部内容更改成枚举,其中将全部内容都固定为只读文件便可。 对象
这是接口和基类的基本和简单定义:
干杯
在Java World这篇文章中很好地解释了
我我的倾向于使用接口来定义接口,即系统设计中指定应如何访问内容的部分。
我会有一个实现1个或多个接口的类并很多见。
我将抽象类用做其余内容的基础。
如下是上述文章JavaWorld.com文章的做者Tony Sintes的摘录,04/20/01
接口与抽象类
选择接口和抽象类不是一个选择。 若是须要更改设计,请使其成为界面。 可是,您可能具备提供某些默认行为的抽象类。 抽象类是应用程序框架内的优秀候选者。
抽象类使您能够定义一些行为。 他们强迫您的子类提供其余人。 例如,若是您有一个应用程序框架,则抽象类能够提供默认服务,例如事件和消息处理。 这些服务容许您的应用程序插入您的应用程序框架。 可是,只有您的应用程序才能执行某些特定于应用程序的功能。 此类功能可能包括启动和关闭任务,这些任务一般取决于应用程序。 所以,抽象基类能够声明抽象的关闭和启动方法,而没必要尝试定义该行为自己。 基类知道它须要那些方法,可是抽象类让您的类认可它不知道如何执行这些动做。 它只知道它必须启动动做。 是时候启动了,抽象类能够调用启动方法。 当基类调用此方法时,Java会调用子类定义的方法。
许多开发人员忘记了定义抽象方法的类也能够调用该方法。 抽象类是建立计划的继承层次结构的绝佳方法。 对于类层次结构中的非叶子类来讲,它们也是一个不错的选择。
类与接口
有人说您应该根据接口定义全部类,可是我认为建议彷佛有些极端。 当我发现设计中的某些东西会常常变化时,我会使用接口。
例如,使用策略模式,您能够将新算法和过程交换到程序中,而无需更改使用它们的对象。 媒体播放器可能知道如何播放CD,MP3和WAV文件。 固然,您不想将这些播放算法硬编码到播放器中; 这将使添加AVI等新格式变得困难。 此外,您的代码中会堆满无用的case语句。 为了增长侮辱性伤害,您每次添加新算法时都须要更新这些案例陈述。 总而言之,这不是一种很是面向对象的编程方式。
使用策略模式,您能够简单地将算法封装在对象后面。 若是这样作,则能够随时提供新的媒体插件。 咱们将其称为插件类MediaStrategy。 该对象将具备一个方法:playStream(Stream s)。 所以,要添加新算法,咱们只需扩展算法类便可。 如今,当程序遇到新的媒体类型时,它只是将流的播放委托给咱们的媒体策略。 固然,您将须要一些管道来正确地实例化所需的算法策略。
这是使用界面的绝佳场所。 咱们使用了策略模式,该模式清楚地代表了设计中将会改变的位置。 所以,您应该将策略定义为接口。 当您但愿对象具备某种类型时,一般应该优先考虑接口而不是继承。 在这种状况下是MediaStrategy。 依靠继承得到类型标识是危险的; 它将您锁定在特定的继承层次结构中。 Java不容许多重继承,所以您不能扩展某些能够提供有用的实现或更多类型标识的东西。
经过def,接口提供了与其余代码进行通讯的层。 默认状况下,类的全部公共属性和方法都实现隐式接口。 咱们还能够将接口定义为一种角色,当任何类都须要扮演该角色时,它必须实现该接口,并根据实现该接口的类为它提供不一样的实现形式。 所以,当您谈论接口时,您在谈论多态性;当您谈论基类时,您在谈论继承。 哎呀的两个概念!
要记住的另外一种选择是使用“具备”关系,也就是“根据”或“组成”实现。 有时候,与使用“是”继承相比,这是一种结构更清晰,更灵活的方法。
从逻辑上讲,“狗”和“猫”都“拥有”一个宠物可能没有逻辑上的意义,但它避免了常见的多重继承陷阱:
public class Pet { void Bathe(); void Train(Trick t); } public class Dog { private Pet pet; public void Bathe() { pet.Bathe(); } public void Train(Trick t) { pet.Train(t); } } public class Cat { private Pet pet; public void Bathe() { pet.Bathe(); } public void Train(Trick t) { pet.Train(t); } }
是的,此示例代表以这种方式执行代码涉及不少代码重复而且缺少优雅。 可是,还应该意识到,这有助于使Dog and Cat与Pet类脱钩(由于Dog and Cat没法访问Pet的私有成员),而且为Dog and Cat继承从其余事物继承的空间- -多是哺乳动物类。
当不须要私人访问而且您不须要使用通用Pet引用/指针来引用Dog和Cat时,最好使用组合。 接口为您提供了通用的参考功能,能够帮助减小代码的冗长性,可是当它们组织得很差时,它们也可使事情变得模糊。 当您须要私有成员访问时,继承颇有用,而且在使用继承时,您将致力于将Dog和Cat类与Pet类高度结合,这是一笔高昂的费用。
在继承,组合和接口之间,没有一种永远正确的方法,它有助于考虑如何将全部三个选项和谐地使用。 在这三者中,继承一般是应该最少使用的选项。