【问题标题】:Reduce code duplication without subclass inheritance在没有子类继承的情况下减少代码重复
【发布时间】:2018-07-08 13:42:30
【问题描述】:

我在玩子类化与接口和组合。当涉及到代码重复时,我最终对一些事情感到困惑。众所周知,在很多情况下,子类化和继承并不是可行的方法,但它在减少代码重复方面是有效的。

接口很强大,如果处理得当,可读性也很好,但我无法理解这样一个事实,即它实际上并没有帮助我减少代码重复。我们最终可能会遇到子类化无效的情况。但是扩展程序的可能性很大,每当我们这样做时,试图维护开放封闭的原则,我们最终会以荒谬的数量进行接口的实现/实现粘贴代码,可能可以通过子类化避免(就代码重复而言)。

我们如何通过接口和组合构建出色的策略,避免一遍又一遍地编写相同的方法?这样我们就可以在保持模块化的同时坚持开放封闭的原则。对于如何快速有效地确定代码重复最终是否值得,我们是否有任何指导方针?

干杯

【问题讨论】:

  • 有好书涵盖了这个问题。我建议您阅读“可重用的面向对象软件的设计模式元素”和“干净的代码”。我发现它们很有趣。
  • 如果您发现自己为同一个接口的每个实现添加了相同的逻辑,那么请考虑使用实现该接口并实现其中大部分常见逻辑的抽象类。

标签: java inheritance interface modularity open-closed-principle


【解决方案1】:

面向对象的建模非常主观,但我唯一能做的就是旧的继承与组合讨论: https://www.thoughtworks.com/insights/blog/composition-vs-inheritance-how-choose

根据您的论点,我相信您经常尝试从两个或多个具有相似代码的类中提取超类,这样它们就可以共享相同的继承方法,而不仅仅是复制它们。尽管从技术上讲,这正是您想要的,但您还应该注意继承语义,因为它将表示 is-a 关系(即,汽车 is-a 车辆,狗 is-a 哺乳动物,报告屏幕 is-a 只读屏幕)。由于 Java 不提供多重继承,如果您的类层次结构增长,您最终可能会受到限制和困惑。

因此,在开始提取超类以供重用之前,请记住,您也可以将这些 code-i-want-to-reuse 单元提取为 part-of其他类(作文)。

对不起,我的概念性示例,但在这里狮子都是哺乳动物和猎人。它们应该自然地继承 Mammals 超类(在哺乳动物中有很多可重用的代码)。由于并非所有哺乳动物都会捕猎,因此我们不想在 Mammals 类上定义新的 hunt() 方法。

此时,您可能正在考虑创建一个新的继承级别:哺乳动物 。但想一想:如果你继续对动物的每一个独特方面都这样做,你将拥有几十个处于复杂和令人困惑的层次结构中的类。除此之外,我们也知道一些爬行动物和鸟类也会狩猎,所以,我们最好将所有狩猎的东西隔离在其他地方。

作为继承的健康替代方案,我们可以定义一个单独的 Hunter 类。要重用它的内容,我们需要做的就是将一个 Hunter 对象作为 DogLion 的成员(一个字段)。如果我们需要将狗和狮子一起视为猎人(多态),我们可以定义一个 CanHunt 接口来对它们进行分组。

查看以下示例:

class Hunter {
   void hunt(){
       System.out.println("i'm hunting...");
   }
}

interface CanHunt{
   Hunter getHunter();
}

class Dog extends Mammals implements CanHunt{
   ...
   Hunter hunter = new Hunter();
   
   @Override
   Hunter getHunter(){
       return hunter;
   }      
   ...
}


class Lion extends Mammals implements CanHunt{
   ...
   Hunter hunter = new Hunter();
   
   @Override
   Hunter getHunter(){
       return hunter;
   }      
   ...
}

这里我们有一个多态示例代码,它要求狗和狮子都做它们的狩猎工作:

...
List<CanHunt> hunters = new LinkedList();
hunters.add(new Dog());
hunters.add(new Lion());

for(CanHunt h:hunters){
  h.getHunter().hunt(); //we don't know if it's a dog or a lion here...
}
...

我希望这个简单的例子能给你一些启发。如果我们不断将其发展为更详细但更灵活的设计,它可能会变得相当复杂。例如,Hunter 类可以是具有不同实现的抽象类,因为狗的狩猎方式与狮子不同,但它们有一些共同的行为。

【讨论】:

    【解决方案2】:

    我觉得这个问题很有趣,因为我们大多数人没有足够的时间阅读和重新阅读我们可以继承的所有类,这可能导致我们编写非常低效的代码,有时甚至“重新发明轮子”最后会比我们看其他课程花费更多的时间。

    我对解决这个问题的建议是创建微服务,即执行特殊工作的小型迷你应用程序,这样您就不必像现在那样使用继承,并且您会知道您的确切输出得到。此外,您还可以在其他应用程序中重复使用这些“微服务”。

    作为旁注,请查看@AntoineDubuis 推荐的书籍,我发现它们会很有帮助。

    【讨论】:

    • 不确定微服务与代码重用有什么关系。您不必为了重用而编写迷你应用程序。在大多数情况下,您只需重用 Java 类即可实现相同目的。
    • 用于在其他应用程序中使用自己或他人创建的类,因此您不必再次编写它们。
    猜你喜欢
    • 2013-03-10
    • 1970-01-01
    • 2017-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-25
    • 2018-05-21
    • 2011-04-11
    相关资源
    最近更新 更多