我的一位新合作者正在审查我写的一些代码告诉我,她不习惯看到直接在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,只要它做到了),直接在代码中使用接口有什么害处吗?我知道这可能在某种程度上是番茄/西红柿的东西,但我很好奇第二种风格的优势,或者第一种风格的缺点,我不知道。
答案 0 :(得分:15)
如果您不需要具有所有实现共有的某些细节的抽象类,那么就不需要抽象类。复杂性通常会添加到应用程序中,因为有些人认为需要支持尚未定义的未来功能。坚持行之有效,稍后重构。
答案 1 :(得分:5)
不,她没经验,不对。首选使用接口,为冗余而编写冗余抽象超类是多余的。
UsesFoo
应该关心接口指定的行为,而不是它的依赖项的超类。
答案 2 :(得分:4)
对我来说“她不习惯”是不够好的理由。请她详细说明。
我个人会使用你的解决方案,因为:
AbstractFoo是多余的,广告在当前情况下没有任何价值。
即使需要AbstractFoo(对于一些额外的功能),我总是使用最低需要的类型:如果GeneralFoo足够,那么我会使用它,而不是从它派生的某个类。
答案 3 :(得分:3)
这仅取决于你的问题。
如果仅使用接口,那么如果所有类都具有相同的方法,则必须以冗余方式实现(或移至Util类)。
另一方面,如果你编写了一个中间抽象类,你就解决了这个问题,但现在你的子类可能不是另一个类的子类,因为Java中没有多重继承。如果已经有必要扩展某些类,那就不可能了。
所以,很快 - 这是一个折衷。在特定情况下使用哪个更好。
答案 4 :(得分:2)
直接在代码中使用接口没有坏处。如果有,Java就没有接口。
直接使用接口的缺点包括无法访问未在接口中实现的类特定方法。对于写得不好的接口或添加了许多“其他”功能的类,这是不可取的,因为你失去了获得所需方法的能力。但是,在某些情况下,这可能反映了创建界面时设计选择不佳。如果没有细节,就很难知道。
直接使用基类的缺点包括最终忽略接口,因为它不经常使用。在极端情况下,界面成为人类附录的代码; “现在但提供很少甚至没有功能”。未使用的接口不太可能更新,因为每个人都会直接使用基本抽象类。这允许您的设计从实际尝试使用该界面的任何人的角度静默腐烂。在极端情况下,无法通过接口处理扩展类来执行某些关键功能。
就个人而言,我赞成通过他们的界面返回类,并通过他们最低的子类在内部存储成员。这提供了类的封装中类的深入了解,迫使人们在外部使用接口(使其保持最新),并且类的封装允许将来可能的替换而不会有太多的麻烦。
答案 5 :(得分:2)
我很好奇第二种风格的优势,或者第一种风格的劣势,我不知道
第一个interfaces
样式的原因:
通常,设计是这样的,接口是概念的公共接口,而抽象类是概念的实现细节。
例如,在集合框架中考虑List
和AbstractList
。 List
真的是客户通常会追求的;更少有人了解AbstractList
,因为它的实现细节可以帮助界面的供应商(实施者),而不是班级的客户(用户)。
界面松散耦合,因此更灵活地支持未来的变化。
使用更清晰代表类的要求的那个,通常是接口。
例如,经常使用List
而不是AbsrtactList
或ArrayList
。使用该界面,未来的维护者可能会更清楚这个类需要某种List
,但它并不特别需要AbstractList
或ArrayList
。如果此类依赖于某些AbstractList
特定属性,即它需要使用AbstractList
方法,则使用AbstractList list = ...
代替List list = ...
可能暗示此代码依赖特定于AbstractList
。
它可以简化测试/模拟以使用更小,更抽象的界面,而不是使用抽象类。
答案 6 :(得分:1)
一些人认为通过AbstractFoo
签名声明变量被认为是不好的做法,因为UsesFoo
类与foo
的某些实现细节相关联。
这导致灵活性降低 - 您无法将foo
的运行时类型与实现GeneralFoo
接口的任何类交换;你只能注入实现AbstractFoo
后代的实例 - 留下一个较小的子集。
理想情况下,像UsesFoo
这样的类应该只能知道他们使用的协作者的接口,而不是任何实现细节。
当然,如果不需要在abstract
中声明任何abstract class AbstractFoo implements GeneralFoo
- 即没有所有子类将重复使用的通用实现 - 那么这只是浪费额外的文件和层次结构中的级别。
答案 7 :(得分:0)
首先,我充分使用抽象和接口类。
我认为在使用界面之前需要看到使用界面的价值。我认为设计方法是,哦,我们有一个类,因此我们应该有一个抽象类,因此我们应该有接口。
首先为什么需要一个接口,其次为什么你有一个抽象类。似乎她可能会添加东西,为了添加东西。解决方案中需要有明确的价值,否则你所谈论的是没有价值的代码。
在法律上你应该看到她的解决方案中的价值。如果没有价值,那么解决方案就是错误的,如果不能向你解释,她就不明白她为什么这么做了。
当您需要从解决方案中获得的复杂性,灵活性或任何感知价值时,简单代码是更好的解决方案和重构。
显示值或删除代码!
哦,还有一件事要看看Java库代码。这是否使用她正在应用的抽象/界面模式.. NO!