首页 > 解决方案 > Java类从多个接口继承而不重复代码

问题描述

想象5个接口:

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

我现在想为狗和猫创建 2 个新类,class MyDog并且class MyCat. 如果我只是做class MyDog implements IDogand class MyCat implements ICat,那么我将在两只动物之间有很多代码重复,这是不必要的,因为IAnimal等的实现ICarnivore在两只动物之间是相同的。

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

PS:接口和依赖/层次结构可能不会改变。

标签: javainheritanceinterfacecode-duplication

解决方案


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

  1. 接口中的代码重复。

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

  1. 实现中的代码重复。

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

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

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

在您的情况下,“AbstractPet”可能是一个选择或“AbstractMammal”。请注意,“哺乳动物”已经是一个抽象概念,所以也许让我们删除多余的“抽象”

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(),这可能(或可能不是)是接口所需的方法之一。

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

“类/抽象类/接口”方法很容易上手;但是,如果您选择的抽象方法在其预期域中并不是真正的共同点,那么您必须为某些域“覆盖”它。这意味着您有一个既“包含”某些行为又不包含的集合。这种不明确的想法会导致代码随着时间的推移变得难以扩展、维护和推理;因为,你一直在收集“例外情况”。

一种异常情况可能很容易处理,但结合两种异常情况,通常意味着评估 4 种情况(都存在,都不存在,以及两个存在另一个)。添加另一个异常情况会使当前存在的场景乘以 2。很快就会发现 6 个异常会导致 64 个场景,其中你永远不会真正测试它们是否正确的代码功能。

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

在实践中,使用类/抽象类/接口并不难;但是,如果您没有看到您需要做些什么来使您的程序易于使用这些工具进行维护,那么您的程序(和您)可能会遭受自我伤害。如果你的简单方法有一天停止工作,请拿出一些纸并开始绘制维恩图。几分钟后,您可能会以一种告诉您需要什么的方式意识到程序模型中的缺陷(剩下的技巧就是让程序的其余部分进入该状态)。

祝你好运!

PS。很抱歉与代码重复位的偏差。基本上,我们都承认有些时候你不能让 Java 的“抽象类”层次结构崩溃成一个在任何情况下都可以轻松使用的链。发生这种情况时,就会发生代码重复。这是 Java 语言的一个弱点。但是,每种编程语言都有弱点。有时这些弱点还不够严重,甚至无关紧要,有时它们与正在解决的问题无关,有时你应该切换编程语言。

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


推荐阅读