【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>
什么时候应该使用接口,什么时候应该使用基类?
如果我不想实际定义方法的基本实现,是否应该始终是一个接口?
如果我有猫狗班。 为什么我要实现IPet而不是PetBase? 我可以理解具有用于ISheds或IBarks(IMakesNoise?)的接口,因为可以将它们逐个放置在每个宠物上,但是我不知道用于普通Pet的接口。
#1楼
我有一个粗略的经验法则
功能:各个部分可能有所不同:接口。
数据和功能,部分将大部分相同,而部分则不同:抽象类。
数据和功能,如果仅进行了少量更改就可以实际使用:普通(具体)类
数据和功能,没有计划的更改:带有final修饰符的普通(具体)类。
数据,可能还有功能:只读:枚举成员。
这是非常粗糙且准备就绪的,并且根本没有严格定义,但是从接口的频谱可以将所有内容更改为枚举,其中将所有内容都固定为只读文件即可。
#2楼
这是接口和基类的基本和简单定义:
- 基类=对象继承。
- 接口=功能继承。
干杯
#3楼
在Java World这篇文章中很好地解释了
我个人倾向于使用接口来定义接口,即系统设计中指定应如何访问内容的部分。
我会有一个实现1个或多个接口的类并不少见。
我将抽象类用作其他内容的基础。
以下是上述文章JavaWorld.com文章的作者Tony Sintes的摘录,04/20/01
接口与抽象类
选择接口和抽象类不是一个选择。 如果需要更改设计,请使其成为界面。 但是,您可能具有提供某些默认行为的抽象类。 抽象类是应用程序框架内的优秀候选者。
抽象类使您可以定义一些行为。 他们强迫您的子类提供其他人。 例如,如果您有一个应用程序框架,则抽象类可以提供默认服务,例如事件和消息处理。 这些服务允许您的应用程序插入您的应用程序框架。 但是,只有您的应用程序才能执行某些特定于应用程序的功能。 此类功能可能包括启动和关闭任务,这些任务通常取决于应用程序。 因此,抽象基类可以声明抽象的关闭和启动方法,而不必尝试定义该行为本身。 基类知道它需要那些方法,但是抽象类让您的类承认它不知道如何执行这些动作。 它只知道它必须启动动作。 是时候启动了,抽象类可以调用启动方法。 当基类调用此方法时,Java会调用子类定义的方法。
许多开发人员忘记了定义抽象方法的类也可以调用该方法。 抽象类是创建计划的继承层次结构的绝佳方法。 对于类层次结构中的非叶子类来说,它们也是一个不错的选择。
类与接口
有人说您应该根据接口定义所有类,但是我认为建议似乎有些极端。 当我发现设计中的某些东西会经常变化时,我会使用接口。
例如,使用策略模式,您可以将新算法和过程交换到程序中,而无需更改使用它们的对象。 媒体播放器可能知道如何播放CD,MP3和WAV文件。 当然,您不想将这些播放算法硬编码到播放器中; 这将使添加AVI等新格式变得困难。 此外,您的代码中会堆满无用的case语句。 为了增加侮辱性伤害,您每次添加新算法时都需要更新这些案例陈述。 总而言之,这不是一种非常面向对象的编程方式。
使用策略模式,您可以简单地将算法封装在对象后面。 如果这样做,则可以随时提供新的媒体插件。 我们将其称为插件类MediaStrategy。 该对象将具有一个方法:playStream(Stream s)。 因此,要添加新算法,我们只需扩展算法类即可。 现在,当程序遇到新的媒体类型时,它只是将流的播放委托给我们的媒体策略。 当然,您将需要一些管道来正确地实例化所需的算法策略。
这是使用界面的绝佳场所。 我们使用了策略模式,该模式清楚地表明了设计中将会改变的位置。 因此,您应该将策略定义为接口。 当您希望对象具有某种类型时,通常应该优先考虑接口而不是继承。 在这种情况下是MediaStrategy。 依靠继承获得类型标识是危险的; 它将您锁定在特定的继承层次结构中。 Java不允许多重继承,因此您不能扩展某些可以提供有用的实现或更多类型标识的东西。
#4楼
通过def,接口提供了与其他代码进行通信的层。 默认情况下,类的所有公共属性和方法都实现隐式接口。 我们还可以将接口定义为一种角色,当任何类都需要扮演该角色时,它必须实现该接口,并根据实现该接口的类为它提供不同的实现形式。 因此,当您谈论接口时,您在谈论多态性;当您谈论基类时,您在谈论继承。 哎呀的两个概念!
#5楼
要记住的另一种选择是使用“具有”关系,也就是“根据”或“组成”实现。 有时候,与使用“是”继承相比,这是一种结构更清晰,更灵活的方法。
从逻辑上讲,“狗”和“猫”都“拥有”一个宠物可能没有逻辑上的意义,但它避免了常见的多重继承陷阱:
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类高度结合,这是一笔高昂的费用。
在继承,组合和接口之间,没有一种永远正确的方法,它有助于考虑如何将所有三个选项和谐地使用。 在这三者中,继承通常是应该最少使用的选项。
来源:oschina
链接:https://my.oschina.net/u/3797416/blog/3145570