为什么用抽象类而不是接口编程是错误的



我正在研究Struts1和Strust2之间的差异,我发现,Struts1中的一个常见问题是编程为抽象类而不是接口。

有人能告诉我,为什么编程到抽象类而不是接口是个问题吗?

在Java中,原因之一是您没有多重继承,所以当您扩展抽象类时,就这样,您无法扩展任何其他类。

解决这一问题的唯一方法是通过一个复杂的依赖关系树,从长远来看,这对您的体系结构非常不利(很难快速弄清楚什么依赖于什么)。

如果你的类C需要公开抽象类AC的接口(广义上的接口),那么你可以让类C扩展AC。但现在你也想让类C公开另一个类AC1的接口……没有简单的方法。你需要使用组合(我实际上更喜欢扩展),或者你必须让AC1扩展AC…或者其他一些奇怪的伏都教才能让它发挥作用。

在我看来,架构的清晰性和可扩展性是人们更喜欢使用接口而不是抽象类来组成解决方案的主要原因。还有一个问题是,您的代码可能会变得多么健壮。如果您从外部包/jar扩展类,您可能会被该特定版本的实现所困扰,因为对抽象类的更改可能会破坏您的代码。

另一方面

在界面使用领域,并非所有的东西都是完美的。在某些情况下,试图成为一个纯粹主义者,只使用没有任何扩展的接口,可能会导致一些不必要的代码重复。真的没有什么神奇的规则。

为了解决这个问题并保持灵活性(不使用唯一可用的扩展),您可以通过组合而不是继承来重用代码。实现一个接口,拥有一个具有公共基本代码的类,使您的"继承"方法(从接口),代理到该公共类(成为类的一个属性),您就可以两全其美。

最后,作为一名开发人员(尤其是Java开发人员),我的目标是有一天能够表达Joshua Bloch以前没有更好地表达的观点,直到今天,我都没能做到这一点,所以我把这个链接留给了Effective Java,它比我能够更好地解释最后一点:

有效的Java

我不熟悉Struts,所以不能评论Struts1的具体问题。然而一般来说,抽象类也包括实现,而接口不能。这个

  • 产生了更多的依赖性,这可能会损害可维护性和可重用性
  • 使它们对更改的弹性降低,而更改又可能以意想不到的方式破坏客户端代码
  • 最后但同样重要的是(正如@pcalcao正确指出的),这种方法不允许从另一个类继承

"错"这个词可能太强了。背景总是很重要的。

为什么非此即彼?你不能两者都做吗?编写一个接口并提供一个具有默认行为的抽象实现,一个lajava.utilcollections。

您只能单独继承实现;接口允许多重继承。因此,这可能是首选接口的原因之一。另一个原因是,您可能并不总是想要默认行为。

对Struts持保留态度。Struts1是2000-2002年第一个web model-2MVC框架。从那时起,Struts2就没有从中学到的所有东西(例如Joshua Bloch和"Effective Java")。Struts2并不是一个彻底的重写,因为它必须保持向后兼容性。

我不会把Struts作为一个好的设计或最佳实践的例子。看看其他地方。

问题

编程到抽象类根本不是一个问题,这取决于如何设计对象关系。

差异

抽象类提供了部分实现,这促进了代码的重用,而接口只提供了方法,而没有任何实现来促进封装。

那么什么时候使用摘要

当它的子类有不确定的实现,并且所有子类的行为的其他部分都相同时,应该使用抽象类。

如果让抽象类实现一个接口,您可以两全其美;您可以选择实现接口,而不是对抽象类进行子类化。这样,您仍然可以对另一个类进行子类化。

使用抽象类没有错,但它可能不是的最佳解决方案

抽象类可能部分实现方法,而接口只声明方法

当它决定提供实现时,使用抽象类。或者两者都做,定义一个接口并(部分)实现一个(抽象)类来实现这个接口

  • 抽象类:IS-A
  • 接口:HAS-A(或者可以做,-able…)

示例:类Dog和类Cat扩展了抽象类Animal,因为两者都是动物(Dog是一种动物)。抽象类Animal将具有类似walk()或sleep()的方法。Class Animal可以用方法wash()实现接口Washable。这种方法将适用于每只动物,因为一只动物会用泥、水、它的舌头、它的猴子同伴的手指。。。

正如前面所说,接口允许更多的模块化。它们在大型项目中是首选,因为它允许更少的代码中断和模拟实现。(例如,我必须使用你的服务,但你没有足够的时间来实现它。如果我们使用接口,我可以用getProductId()方法创建这个mock impl,它总是返回相同的数字。当你完成任务时,我们以你的实际服务来嘲弄我。)。模型非常适合测试。

实例:

  • 装饰图案(抽象类):http://en.wikipedia.org/wiki/Decorator_pattern
  • 策略模式(界面):http://en.wikipedia.org/wiki/Strategy_pattern

除非抽象类直接公开公共字段,否则如果定义了一个包含抽象类的所有公共成员的接口,并让该类实现它,那么就可以使用接口类型的变量或参数来完成几乎所有操作,这与使用抽象类类型可以完成的操作相同。另一方面,如果有必要定义一个类型,它可以做抽象类可以做的一切,但它是从其他基类派生的,那么这种类型将可用于期望接口类型参数的例程,而不可用于期望抽象类类型的例程。

简而言之,接口类型的变量或参数在某些方面优于实现接口并具有相同公共成员的抽象类型;它几乎毫不逊色。因此,应该在没有充分理由的情况下使用接口类型。

最新更新