这是一个非常基本的OO问题,但我很好奇我是否已经过火了。
让我们假设以下层次结构,这是我通常会创建的示例:
IVehicle
- IFlyable
- IPlane
- Plane
- IHelicopter
- Helicopter
- IDrivable
- ICar
- Car
- ITruck
- Truck
接口IPlane
,IHelicopter
,ICar
和ITruck
是否必要,或者这些类是否应该从IFlyable
和IDriveable
直接实施?我正在寻找“最佳oo做法”的答案。
答案 0 :(得分:11)
每当我看到“一个例子的抽象”时我都会怀疑 - 这个抽象层次真的给你增值吗?有时它是,请注意 - 例如,它通过Mocks简化测试(如果与依赖注入一起使用 - 例如,即使实现ICar的唯一“真正”类是类Car,你可能仍然有一个MockCar实现被注入到ICar客户端,以允许更快,更彻底的检查。)
但是,除非你使用这种优秀的测试实践和模式,否则我会怀疑具有单个具体示例的抽象 - 只有一个实现的接口,只有一个具体子类的抽象类。什么是“额外的抽象层”为你买单,为了付出额外的复杂性/间接性,如果只有一个可能的实现基于所谓的抽象......?
答案 1 :(得分:1)
继承级别始终基于完美解决需求所需的要求或抽象级别。
通过具有应用程序的未来可扩展性,您可以使用Interfaces IPlane,IHelicopter。如果您确定应用程序不会扩展其功能,那么接口中的这种深度不需要,这将导致冗长的编码并且管理组件将是困难的。
答案 2 :(得分:0)
理论上,从OO的角度来看,这种方法是理想的,因为它最大化了类之间的解耦。在实践中,如果您最终只能使用一对一的界面和实现,那么这可能是一种不必要的痛苦。它变得痛苦的原因是,当浏览使用源时,有一个额外的步骤来跟踪实现类,而不是能够立即识别它。
如果您计划为界面实施多个实现,请按照您显示的方式进行。如果你只有一个实现,请离开界面直到你需要它。
答案 3 :(得分:0)
这完全取决于。如果您需要区分飞机和直升机,那么您可能仍然需要使用IPlane,但如果您不需要或者您只需要一架飞机,那么它可能会“过度杀伤”。
例如,您甚至可以删除IFlyable
:
interface IDrivable : IUsableItem
interface IPlane : IDrivable
class Cessna : IPlane
class Boeing : IPlane
interface IVehicle : IDrivable
class Semitrailer : IVehicle
class MorrisMinor : IVehicle
答案 4 :(得分:0)
在您的示例中执行所有这些接口的一个论点是,它使单元测试变得更加容易。例如,制作一个MockCar
非常简单,因为您可以简单地实现ICar
并发送MockCar
实例而不是真实的Car
实例。当然,这假设您希望Car
对象在任何地方将变量声明为类型ICar
。
正如 sipwiz 所说,它还会减少耦合并增加类树中的内聚力(两者都是好事)。这将帮助您以简单的代码导航为代价,制作更清晰,更简洁的代码。
话虽这么说,这对于一个简单的应用来说可能有点过分了;过度工程的经典展示。为了复杂性,为了鲁棒性而在复杂性和复杂性之间找到了平衡点。这种平衡只是随着开发人员的不断获得的专业知识而来。真的没有正确的答案。如果您发现所有这些类和接口都在减慢您的速度并使事情变得不必要地复杂化,那么就不要这样做;使代码更简单,直到需要更复杂。
答案 5 :(得分:0)
这取决于预期的用途。一般而言,我有两个规则来管理我的层次结构。
第一个是在声明参数或变量时总是使用接口,你可以解决上面的问题,因为你永远不必声明一个Plane变量。当然,如果您的飞机舱没有延长IPlane提供的合同,那将是正确的,这将是一个严重的“嗯!”
第二条规则是我只介绍我期望使用的接口。所以在上面的例子中如果我不期望写一行代码来处理飞机和汽车 有点像
对于车辆中的每个IVehicle v v.someMethodDefinedByIVehicle()
那会告诉我,在我的域名中,汽车和飞机不一样,我的代码不应该试图让它们如此。同样适用于IFlyable amd IDrivable。 IFlyable和IDrivable实际上是一个很好的例子,说明为什么你应该只考虑自己的域名。一架飞机肯定是可以驾驶的(否则我无法在没有帮助的情况下滑行),但仅仅因为它的驾驶并不意味着它对道路的影响,而且有些汽车实际上是可以飞行的,但在大多数领域都是可以忽略不计的。
答案 6 :(得分:0)
如果您正在使用IoC容器或某些模拟框架或NHibernate,那么拥有几乎所有内容的界面可能非常有用。此外,接口的使用鼓励了松散耦合且通常更好的可维护的软件开发风格。
但是如果在具体情况下它不合适/不合适,那么你很容易过度使用重复的继承层次结构。
你必须为每个项目问自己这个问题:在一个简单的类中使用接口是否会增加价值,或者它是否可能在可预见的未来在某个地方提供价值。如果你'不确定,不要使用接口 - 如果有必要,可以使用现代重构工具轻松提取它们。