简体   繁体   English

Java类从多个接口继承而无需重复代码

[英]Java class inheriting from multiple interfaces without code duplication

Imagine 5 interfaces:想象5个接口:

  1. IAnimal
  2. ICanivore extends IAnimal ICanivore扩展了IAnimal
  3. IPet extends IAnimal IPet扩展了IAnimal
  4. IDog extends IPet , ICarnivore IDog扩展了IPet , ICarnivore
  5. ICat extends IPet , ICarnivore ICat扩展了IPet , ICarnivore

I now want to create 2 new classes for dogs and cats, class MyDog and class MyCat .我现在想为狗和猫创建 2 个新类, class MyDogclass MyCat If I simply do class MyDog implements IDog and class MyCat implements ICat , then I will have a lot of code duplication between the two animals, which is unnecessary since the implementations for IAnimal , ICarnivore etc. will be identical between the two animals.如果我只是简单地执行class MyDog implements IDogclass MyCat implements ICat ,那么我将在两种动物之间产生大量代码重复,这是不必要的,因为IAnimalICarnivore等的实现在两种动物之间是相同的。

Is there a way to implement the methods of every interface only once?有没有办法只实现每个接口的方法一次?

PS: Interfaces and dependecies/hierarchies may not be changed. PS:接口和依赖/层次结构可能不会改变。

Let's clarify about the code duplication, to make certain we are talking about the same things, and why code duplication is bad:让我们澄清一下代码重复,以确保我们在谈论相同的事情,以及为什么代码重复很糟糕:

  1. Code duplication in the interfaces.接口中的代码重复。

Both of these classes will use a number of similar interfaces.这两个类都将使用许多类似的接口。 The interface is written once and used many times.接口一次编写,多次使用。 This is desirable sometimes;这有时是可取的; because, modifying the interface triggers modifications in all the child classes.因为,修改接口会触发所有子类中的修改。 This is also sometimes undesirable, as a modification might mean adding code to classes that you would rather not modify (but have to be modified due to the interface change being needed by an unrelated class).这有时也是不可取的,因为修改可能意味着将代码添加到您不想修改的类(但由于不相关的类需要更改接口而必须修改)。

  1. Code duplication in the implementation.实现中的代码重复。

Both of these classes will implement a number of similar methods.这两个类都将实现许多类似的方法。 The implementation of specific methods is often written many times.具体方法的实现往往要写很多遍。 This is typically undesirable due to the need for software maintenance.由于需要软件维护,这通常是不可取的。 If a bug is found in one implementation of a commonly copied method, one would need to implement the fix in every copy, where missed copies would keep the bug in the code longer than desired.如果在常见复制方法的一个实现中发现错误,则需要在每个副本中实现修复,如果丢失的副本会使代码中的错误比预期的要长。 On the other hand, if these copies are meant to have different lifecycles, then one might actually desire this duplication, as a fix to one module (a larger collection of code with its own update schedule) wouldn't mandate releasing all modules (where the bug may have little or no impact).另一方面,如果这些副本具有不同的生命周期,那么人们实际上可能希望进行这种复制,因为对一个模块(具有自己的更新计划的更大代码集合)的修复不会强制要求发布所有模块(其中该错误可能影响很小或没有影响)。

So Code Duplication is generally bad;所以代码重复通常很糟糕; but, it is not always bad.但是,它并不总是坏的。 The scenarios where it is bad are far greater than those scenarios where it makes sense.糟糕的场景远远大于那些有意义的场景。

To remove code duplication, the traditional Java approach is to use abstract classes.为了消除代码重复,传统的 Java 方法是使用抽象类。 You find the duplicates that are in common, and create an abstract class.您找到共同的重复项,并创建一个抽象类。 The naming convention is often 'AbstractDog' or 'DefaultDog';命名约定通常是“AbstractDog”或“DefaultDog”; both of which are pretty bad naming conventions (When you finally do the deep dive into naming) This abstract class will have the common implementations for all of the methods that it's category should use.这两个都是非常糟糕的命名约定(当你最终深入研究命名时)这个抽象类将具有它的类别应该使用的所有方法的通用实现。

In your case, 'AbstractPet' might be a choice or 'AbstractMammal'.在您的情况下,“AbstractPet”可能是一个选择或“AbstractMammal”。 Note that 'Mammal' is already an abstract idea, so maybe let's just drop the redundant '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();

}

Now all of your mammals don't need to implement getHeartbeatsPerMinute() and that may (or may not) be one of the interface's required methods.现在您的所有哺乳动物都不需要实现getHeartbeatsPerMinute()并且这可能(也可能不是)是接口所需的方法之一。

The main problem with this kind of modeling isn't the language.这种建模的主要问题不是语言。 The main problem is that people often under-evaulate the classes, abstract classes, and interfaces and come up with combinations that fail to subdivide into mathematical Sets (I'm not talking the Java Sets, but the discrete math "Set theory" rules).主要问题是人们经常低估类、抽象类和接口,并提出无法细分为数学集合的组合(我不是在谈论 Java 集合,而是离散数学“集合论”规则) .

It's easy to get started with the "Class / Abstract Class / Interface" approach;使用“类/抽象类/接口”方法很容易上手; but, if you choose an abstract method that isn't really a common denominator across it's intended domain, you have to "override" it for some of the domain.但是,如果您选择的抽象方法在其预期域中并不是真正的公分母,则必须为某些域“覆盖”它。 This means you have a Set that both "includes" some behavior and at the same time doesn't.这意味着您有一个既“包含”某些行为又不包含某些行为的 Set。 This kind of unclear thinking leads to code that becomes hard to extend, maintain, and reason about over time;这种不清楚的想法导致代码随着时间的推移变得难以扩展、维护和推理; because, you keep collecting "exception cases".因为,您一直在收集“异常情况”。

One exception case might be easy to deal with, but combining two exception cases, typically means evaluating 4 scenrios (both present, both absent, and two where one other is present).一种异常情况可能很容易处理,但将两种异常情况结合起来,通常意味着评估 4 种情况(都存在、都没有,以及存在另一种情况的两种情况)。 Adding in another exception case multiplies the scenarios currently in existance by two.添加另一个例外情况会将当前存在的场景乘以 2。 Quickly one can see that six exceptions will lead to 64 scenarios, of which you will never really test them all for correct code functionality.很快就可以看到六个异常将导致 64 个场景,您永远不会真正测试所有这些场景以获得正确的代码功能。

So, go ahead and experiment with abstract classes;所以,继续尝试抽象类; but, be aware that each class acts like a set of it's instances.但是,请注意,每个类都像一组它的实例。 Also be aware that unlike real set theory, Java (and other languages) don't have a "not in this set" expression.还要注意,与真正的集合论不同,Java(和其他语言)没有“不在这个集合中”的表达。 Be aware that while you can combine Interfaces freely, you can't combine abstract classes (this limits the kinds of divisions of types to a subset of set theory, which is easy to implement (and sufficient for most problems). And keep in mind that it is both possible to design a (set) class hierarchy that fails to divide up the concepts according to the real worls in addition to designing a class hierarcy that fails to compile within the scope of Java's rules.请注意,虽然您可以自由组合接口,但不能组合抽象类(这将类型划分的种类限制为集合论的一个子集,这很容易实现(并且足以解决大多数问题)。并且请记住除了设计一个无法在Java规则范围内编译的类层次结构之外,还可以设计一个无法根据现实世界划分概念的(设置)类层次结构。

In practice it really isn't hard to use classes / abstract classes / interfaces;实际上,使用类/抽象类/接口并不难; however, if you aren't given a glimpse of what you need to do to make your programs easily maintainable with these tools, you programs (and you) will probably suffer from self-inflicted harm.但是,如果您不了解使用这些工具轻松维护您的程序需要做什么,您的程序(和您)可能会遭受自我伤害。 If your simple approaches stop working one day, break out some paper and start drawing Venn diagrams.如果您的简单方法有一天不起作用,请拿出一些纸并开始绘制维恩图。 In a few minutes you'll probably realize the flaws in the program's model in a way that tells you what you need (the rest of the trick is getting the rest of the program into that state).几分钟后,您可能会以一种告诉您需要什么的方式意识到程序模型中的缺陷(其余的技巧是让程序的其余部分进入该状态)。

Good luck!祝你好运!

PS.附注。 Sorry about the deviation from the code duplication bits.很抱歉与代码重复位的偏差。 Basically, we all accept that there are some times when you can't make Java's 'abstract class' hierarcy collapse to a chain that is easily usable in ever scenario.基本上,我们都承认,有时您无法将 Java 的“抽象类”层次结构折叠为在任何情况下都可以轻松使用的链。 When that happens, code duplication will occur.当这种情况发生时,就会发生代码重复。 That's a weakness of the Java language;这是 Java 语言的一个弱点。 but, every programming language has weaknesses.但是,每种编程语言都有弱点。 Sometime those weaknesses aren't bad enough to even matter, sometimes they are irrelevant to the problem being solved, and sometimes you should switch programming languages.有时这些弱点还不够严重,有时它们与要解决的问题无关,有时您应该切换编程语言。

The decision to switch has to do with accepting that this problem needs features where a weakness directly hurts, but keep in mind that with the switch, you get an entirely different set of weaknesses, often overlapping where teh previous language had strengths.转换的决定与接受这个问题需要弱点直接伤害的特性有关,但请记住,通过转换,你会得到一组完全不同的弱点,通常与以前语言的强项重叠。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM