Java代码样式 - 接口与抽象类

时间:2011-02-16 15:55:25

标签: java coding-style

我的一位新合作者正在审查我写的一些代码告诉我,她不习惯看到直接在Java代码中使用的接口,例如:

public interface GeneralFoo { ... }

public class SpecificFoo implements GeneralFoo { ... }

public class UsesFoo {
     GeneralFoo foo = new SpecificFoo();
}

相反,期待看到

public interface GeneralFoo { ... }

public abstract class AbstractFoo implements GeneralFoo { ... }

public class SpecificFoo extends AbstractFoo { ... }

public class UsesFoo {
     AbstractFoo foo = new SpecificFoo();
}

我可以看到这个模式何时有意义,如果所有SpecificFoos通过AbstractFoo共享功能,但是如果各种Foos有完全不同的内部实现(或者我们不关心特定的Foo如何做Bar,只要它做到了),直接在代码中使用接口有什么害处吗?我知道这可能在某种程度上是番茄/西红柿的东西,但我很好奇第二种风格的优势,或者第一种风格的缺点,我不知道。

8 个答案:

答案 0 :(得分:15)

如果您不需要具有所有实现共有的某些细节的抽象类,那么就不需要抽象类。复杂性通常会添加到应用程序中,因为有些人认为需要支持尚未定义的未来功能。坚持行之有效,稍后重构。

答案 1 :(得分:5)

不,她没经验,不对。首选使用接口,为冗余而编写冗余抽象超类是多余的。

UsesFoo应该关心接口指定的行为,而不是它的依赖项的超类。

答案 2 :(得分:4)

对我来说“她不习惯”是不够好的理由。请她详细说明。

我个人会使用你的解决方案,因为:

  1. AbstractFoo是多余的,广告在当前情况下没有任何价值。

  2. 即使需要AbstractFoo(对于一些额外的功能),我总是使用最低需要的类型:如果GeneralFoo足够,那么我会使用它,而不是从它派生的某个类。

答案 3 :(得分:3)

这仅取决于你的问题。

如果仅使用接口,那么如果所有类都具有相同的方法,则必须以冗余方式实现(或移至Util类)。

另一方面,如果你编写了一个中间抽象类,你就解决了这个问题,但现在你的子类可能不是另一个类的子类,因为Java中没有多重继承。如果已经有必要扩展某些类,那就不可能了。

所以,很快 - 这是一个折衷。在特定情况下使用哪个更好。

答案 4 :(得分:2)

直接在代码中使用接口没有坏处。如果有,Java就没有接口。

直接使用接口的缺点包括无法访问未在接口中实现的类特定方法。对于写得不好的接口或添加了许多“其他”功能的类,这是不可取的,因为你失去了获得所需方法的能力。但是,在某些情况下,这可能反映了创建界面时设计选择不佳。如果没有细节,就很难知道。

直接使用基类的缺点包括最终忽略接口,因为它不经常使用。在极端情况下,界面成为人类附录的代码; “现在但提供很少甚至没有功能”。未使用的接口不太可能更新,因为每个人都会直接使用基本抽象类。这允许您的设计从实际尝试使用该界面的任何人的角度静默腐烂。在极端情况下,无法通过接口处理扩展类来执行某些关键功能。

就个人而言,我赞成通过他们的界面返回类,并通过他们最低的子类在内部存储成员。这提供了类的封装中类的深入了解,迫使人们在外部使用接口(使其保持最新),并且类的封装允许将来可能的替换而不会有太多的麻烦。

答案 5 :(得分:2)

  

我很好奇第二种风格的优势,或者第一种风格的劣势,我不知道

第一个interfaces样式的原因:

  1. 通常,设计是这样的,接口是概念的公共接口,而抽象类是概念的实现细节。

    例如,在集合框架中考虑ListAbstractListList真的是客户通常会追求的;更少有人了解AbstractList,因为它的实现细节可以帮助界面的供应商(实施者),而不是班级的客户(用户)。

  2. 界面松散耦合,因此更灵活地支持未来的变化。

  3. 使用更清晰代表类的要求的那个,通常是接口。

    例如,经常使用List而不是AbsrtactListArrayList。使用该界面,未来的维护者可能会更清楚这个类需要某种List,但它并不特别需要AbstractListArrayList。如果此类依赖于某些AbstractList特定属性,即它需要使用AbstractList方法,则使用AbstractList list = ...代替List list = ...可能暗示此代码依赖特定于AbstractList

  4. 的内容
  5. 它可以简化测试/模拟以使用更小,更抽象的界面,而不是使用抽象类。

答案 6 :(得分:1)

一些人认为通过AbstractFoo签名声明变量被认为是不好的做法,因为UsesFoo类与foo的某些实现细节相关联。

这导致灵活性降低 - 您无法将foo的运行时类型与实现GeneralFoo接口的任何类交换;你只能注入实现AbstractFoo后代的实例 - 留下一个较小的子集。

理想情况下,像UsesFoo这样的类应该只能知道他们使用的协作者的接口,而不是任何实现细节。

当然,如果不需要在abstract中声明任何abstract class AbstractFoo implements GeneralFoo - 即没有所有子类将重复使用的通用实现 - 那么这只是浪费额外的文件和层次结构中的级别。

答案 7 :(得分:0)

首先,我充分使用抽象和接口类。

我认为在使用界面之前需要看到使用界面的价值。我认为设计方法是,哦,我们有一个类,因此我们应该有一个抽象类,因此我们应该有接口。

首先为什么需要一个接口,其次为什么你有一个抽象类。似乎她可能会添加东西,为了添加东西。解决方案中需要有明确的价值,否则你所谈论的是没有价值的代码。

在法律上你应该看到她的解决方案中的价值。如果没有价值,那么解决方案就是错误的,如果不能向你解释,她就不明白她为什么这么做了。

当您需要从解决方案中获得的复杂性,灵活性或任何感知价值时,简单代码是更好的解决方案和重构。

显示值或删除代码!

哦,还有一件事要看看Java库代码。这是否使用她正在应用的抽象/界面模式.. NO!