Java class 从多个接口继承,没有代码重复

Java class inheriting from multiple interfaces without code duplication

假设有 5 个接口:

  1. IAnimal
  2. ICanivore 扩展 IAnimal
  3. IPet 扩展 IAnimal
  4. IDog 扩展 IPetICarnivore
  5. ICat 扩展 IPetICarnivore

我现在想为狗和猫创建 2 个新的 类,class MyDogclass MyCat。如果我简单地做 class MyDog implements IDogclass MyCat implements ICat,那么我将在这两个动物之间有很多代码重复,这是不必要的,因为 IAnimalICarnivore 等的实现。两只动物将完全相同。

有没有办法让每个接口的方法只实现一次?

PS: 接口和 dependecies/hierarchies 不能更改。

让我们澄清一下代码重复,以确保我们在谈论相同的事情,以及为什么代码重复不好:

  1. 接口中的代码重复。

这两个 classes 将使用许多相似的界面。接口一次编写,多次使用。有时这是可取的;因为,修改界面会触发所有子 classes 中的修改。这有时也是不可取的,因为修改可能意味着向 classes 添加您不想修改的代码(但由于不相关的 class 需要更改接口而必须修改)。

  1. 实现中的代码重复。

这两个 classes 都将实现许多类似的方法。具体方法的实现往往要写很多遍。由于需要软件维护,这通常是不希望的。如果在通常复制的方法的一个实现中发现错误,则需要在每个副本中实施修复,其中丢失的副本会使代码中的错误保持的时间比预期的要长。另一方面,如果这些副本意味着具有不同的生命周期,那么人们可能实际上希望这种复制,因为对一个模块的修复(具有自己的更新计划的更大的代码集合)不会强制发布所有模块(其中该错误可能影响很小或没有影响)。

所以代码重复通常是不好的;但是,它并不总是坏的。坏的场景远远大于有意义的场景。

要消除代码重复,传统的Java 方法是使用抽象classes。您找到共同的重复项,并创建一个摘要 class。命名约定通常是 'AbstractDog' 或 'DefaultDog';这两个都是非常糟糕的命名约定(当你最终深入研究命名时)这个抽象 class 将具有它的类别应该使用的所有方法的通用实现。

在您的情况下,'AbstractPet' 可能是一个选择或 'AbstractMammal'。请注意 'Mammal' 已经是一个抽象的想法,所以也许让我们放弃多余的 'Abstract'

public abstract class Mammal extends IAnimal {

    private float heartbeatsPerMinute;

    public Mammal(float heartbeatsPerMinute) {
        this.heartbeatsPerMinute = heartbeatsPerMinute;
    }

    public float getPulse() {
        return heartbeatsPerMinute;
    }

    public abstract getCommonName();

}

现在您所有的哺乳动物都不需要实现 getHeartbeatsPerMinute() 并且这可能(也可能不是)接口的必需方法之一。

这种建模的主要问题不是语言。主要问题是人们经常 under-evaulate classes、抽象 classes 和接口,并提出无法细分为数学集合的组合(我不是在说 Java 集合,但离散数学“集合论”规则)。

使用“Class / Abstract Class / Interface”方法很容易上手;但是,如果您选择的抽象方法在其预期域中并不是真正的公分母,则必须为某些域“覆盖”它。这意味着您有一个既“包含”某些行为又同时不包含的集合。这种不清晰的思维会导致代码随着时间的推移变得难以扩展、维护和推理;因为,你一直在收集“例外情况”。

一个异常情况可能很容易处理,但结合两个异常情况,通常意味着评估 4 种情况(既存在,又不存在,两个存在一个)。添加另一个异常情况会将当前存在的场景乘以二。很快就会发现 6 个异常将导致 64 个场景,您永远不会真正测试所有这些场景的正确代码功能。

所以,继续尝试抽象 classes;但是,请注意每个 class 就像它的一组实例一样。还要注意,与实集理论不同,Java(和其他语言)没有“不在这个集合中”的表达。请注意,虽然您可以自由组合接口,但不能组合抽象 classes(这将类型的划分限制在集合论的子集中,这很容易实现(并且足以解决大多数问题) . 请记住,除了设计无法在其中编译的 class 层次结构之外,还可以设计一个无法根据现实世界划分概念的 (set) class 层次结构Java 规则的范围。

在实践中使用 classes / abstract classes / interfaces 并不难;但是,如果您不了解需要做什么才能使用这些工具轻松维护您的程序,那么您的程序(和您自己)可能会受到 self-inflicted 的伤害。如果有一天你的简单方法失效了,拿出一些纸开始画维恩图。几分钟后,您可能会以一种告诉您需要什么的方式意识到程序模型中的缺陷(剩下的技巧是让程序的其余部分进入该状态)。

祝你好运!

PS。抱歉有偏差从代码重复位。基本上,我们都承认,有时您无法将 Java 的 'abstract class' 层次结构折叠成一条在任何情况下都易于使用的链。当发生这种情况时,就会发生代码重复。这是 Java 语言的弱点;但是,每种编程语言都有弱点。有时这些弱点甚至还不够严重,有时它们与要解决的问题无关,有时您应该切换编程语言。

转换的决定与接受这个问题需要的功能有直接伤害的弱点有关,但请记住,通过转换,您会遇到一组完全不同的弱点,通常会与以前的语言重叠优势。