接口与基类

我是研究僧i 提交于 2019-12-21 10:39:06

【推荐】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类高度结合,这是一笔高昂的费用。

在继承,组合和接口之间,没有一种永远正确的方法,它有助于考虑如何将所有三个选项和谐地使用。 在这三者中,继承通常是应该最少使用的选项。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!