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

时间:2012-03-28 09:16:31

标签: java oop coding-style

我正在浏览Struts1和Strust2之间的差异,我发现, Struts 1中的一个常见问题是编写抽象类而不是接口。

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

10 个答案:

答案 0 :(得分:6)

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

解决这个问题的唯一方法是通过一个复杂的依赖关系树,从长远来看这对你的架构非常糟糕(很难快速找出什么取决于什么)。

如果你的C类需要暴露抽象类AC的接口(广义上的接口),那么你可以使C类扩展AC。但是现在你也希望你的C类暴露另一个类AC1的接口......并且没有简单的方法可以做到这一点。你需要求助于合成(我实际上更喜欢扩展),或者你必须让AC1扩展AC ...或其他一些奇怪的伏都教才能让它发挥作用。

在我看来,架构清晰度和可扩展性是人们更喜欢使用Interfaces而不是Abstract Classes来构建解决方案的主要原因。还有一个问题是您的代码可能会变得多么强大。如果从外部包/ jar扩展类,则可能会遇到该实现的特定版本,因为对Abstract类的更改可能会破坏您的代码。

另一方面......

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

要解决此问题并仍然保持灵活性(不会因为您唯一可用的扩展而牺牲自己),您可以通过组合而不是继承来重用代码。实现一个接口,一个具有公共基本代码的类,使用“继承”方法(从接口),代理到该公共类(它成为您的类的属性),并且您拥有两全其美的优势。

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

Effective Java

答案 1 :(得分:3)

我对Struts不熟悉,所以不能评论Struts 1的具体问题。但是, 通常,抽象类也包括实现,哪些接口不能。此

  • 创建了更多依赖项,这可能会损害可维护性和可重用性,
  • 使他们对变更的抵抗力降低,从而可能以意想不到的方式破坏客户端代码,
  • 最后但并非最不重要(正如@pcalcao正确指出的那样),这种方法不允许从另一个类继承。

答案 2 :(得分:3)

错误可能过于强烈。语境总是很重要。

为什么要么/或?你不能两个都做吗?编写一个接口并提供一个带有默认行为的抽象实现,即la java.util集合。

你只能单独继承实施;接口允许多重继承。这可能是偏好接口的一个原因。另一个原因是你可能并不总是想要默认行为。

带上一粒盐的Struts。 Struts 1是2000 - 2002年第一个Web模型-2 MVC框架。它没有从那时学到的所有东西的好处(例如Joshua Bloch和“Effective Java”。)Struts 2并不是那么激进的重写,因为它必须保持向后兼容性。

我不会把Struts作为优秀设计或最佳实践的例子。看别处。

答案 3 :(得分:1)

有一个关于SO的答案的精彩问题"编程到界面":

What does it mean to "program to an interface"?

这不回答问题,但答案中描述的所有好处都会被抽象类丢失,因为它们(可能)提供(部分)实现。

答案 4 :(得分:1)

此链接可帮助您选择何时通过界面或抽象类进行编程:Interfaces vs Abstract Classes

答案 5 :(得分:1)

问题?

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

差分

Abstract Class提供了部分实现,它促进了代码的重用,而Interface只提供了没有任何实现的方法来促进封装。

那么何时使用摘要?

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

答案 6 :(得分:0)

如果您的抽象类实现了接口,那么您可以充分利用这两个世界;而不是子类化抽象类,您可以选择实现接口。这样你仍然可以继承另一个类。

答案 7 :(得分:0)

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

抽象类可以部分实现方法,而接口不会声明方法

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

答案 8 :(得分:0)

简而言之,从概念上讲,它通常被描述为:

  • 抽象类:IS-A
  • 接口:HAS-A(或can-do,-able ......)

实施例: Class Dog和class Cat扩展抽象类Animal,因为它们都是动物(Dog是动物)。 抽象类Animal有一个方法,比如walk()或sleep()。 Class Animal可以用方法wash()实现接口Washable。这种方法将针对每只动物实施,因为人们会使用泥,水,舌头,他的同伴的手指......

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

实际例子:

答案 9 :(得分:0)

除非抽象类直接公开公共字段,否则如果定义一个包含抽象类的所有公共成员的接口并让类实现它,那么就可以使用变量或参数来实现所有内容。可以使用抽象类类型进行的接口类型。另一方面,如果有必要定义一个类型,它可以完成抽象类可以做的所有事情,但是它派生自其他一些基类,这样的类型将可用于期望接口类型参数的例程,但是而不是那些期望抽象类类型。

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