委托对象会产生重复的代码:(继承 vs 委托)

3
我遇到了一个关于面向对象Java的情况,让我在为“可测试性”结构化代码时感到困惑。我所做的研究大多指向了委托,这是一种很酷、直接的模式,然而,我认为它会导致大量重复的代码。
总之,我有一个类,其中包含一些冗长的方法。我有一个第二个类,'扩展'了上述冗长的类并覆盖了其中的一些方法。第二个类在覆盖原始方法时调用了super方法(因此第一个类对于减少代码是完全必要的)。
例如:简化后的父类:
public class ParentClass {

  public void methodX(final int source, final int target) {
   ++++
   ++++
   ++++
  }
}

裸露的子元素:
public class ChildClass extends ParentClass {

  @Override
  public void methodX(final int source, final int target) {
    super.methodX(source, target)
    ****
    ****
    **** // extra logic
  }
}

我的想法和问题:

为 ChildClass(扩展并重写其父类方法的类)编写测试类非常困难,因为我们调用了一个超级方法!超级方法在避免重复测试代码方面非常关键,即测试 ParentClass 的代码将必须在 ChildClass 中使用。我可以使用 Mockito 手动模拟许多这些内部方法,但这绝对似乎是可怕的做法。

我的问题:

这是使用超级方法或扩展类的公平方式吗?是否应该完全避免使用子类(组合>继承)? 在测试子类时是否有必要测试 ParentClass? 应使用哪些设计模式适当地重构代码以避免此情况?


1
尽管组合比继承更受欢迎,但在Java中设置委托到组合的样板代码不幸地是必需的 - 这太糟糕了。有一些语言可以自动为您执行此委派 - 例如,JVM语言Kotlin和我相信golang也是如此。专注于减少Java样板代码的Project Lombok具有“实验性”的委派功能,您可能需要查看:https://projectlombok.org/features/Delegate.html - jmrah
2个回答

1
我认为你的输入中存在各种“气味”:
  • 你开始违反了Java编码约定。类名以大写字母开头,但这只是一个侧面注意点。
  • 我看到的第一个真正的问题是:在子类重写中有多个methodX()的实现;并且使调用super实现成为你的子类重写的必要部分。这在你的类之间引入了非常紧密的耦合。

我会从这一点开始,并尝试摆脱它;但如果你不能或不想这样做,则绝对必要确保你不要“模拟”掉那个super调用。你希望确保你的子类测试运行它们所依赖的代码。

相反的,如果你发现你无法合理地测试你的子类而不使用那些mocking技巧,那么这表明你的代码库中存在问题。


关于耦合的观点很好。我们假设超级调用作为扩展类的本地部分,因此测试同样重要。您如何建议我们“解耦”这些类?我知道我们可以创建一个帮助程序类。还有其他设计更改可以遵循某种模式吗? - Chad Van De Hey
我今天快要结束工作了。我发现你的例子有点过于不具体,让我难以想出好的创意。但是如果你愿意添加“更多代码”并给我留言,明天我可能会看一下。 - GhostCat
随时添加评论都可以(您的回复已经足够且受到赞赏)。我将添加一些示例代码,但鉴于我的情况,该代码的内容并不重要... 实际上,所有代码都可以是打印语句,这并没有什么区别。 - Chad Van De Hey

1
是的,这是一个公平的方式。
扩展意味着定义一个“是一个”关系。
马自达是一辆汽车。
因此,测试马自达是否能够实际执行汽车所做的一切是公平(也是必要的),并且马自达重用汽车的任何东西都是可以接受的。
为了避免(或最小化)CarTest和MazdaTest之间的代码重复,您可以使用CarTestHelper,并将所有要重用的测试代码放在其中,使用静态的帮助方法。

我同意你的观点。我认为你最好的补充是助手类。 - Chad Van De Hey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接