这对此question来说是一个后续问题。
假设我有一个继承树,如下所示:
Car -> Ford -> Mustang -> MustangGT
为这些类的每个定义接口是否有好处?例如:
ICar -> IFord -> IMustang -> IMustangGT
我可以看到其他类(如Chevy
)可能希望实现Icar
或IFord
,甚至可能IMustang
,但可能不是IMustangGT
因为它是如此具体。在这种情况下接口是多余的吗?
另外,我认为任何想要实现IFord
的类肯定希望通过继承Ford
来使用它的一个继承,以免重复代码。如果这是给定的,那么实施IFord
会带来什么好处?
答案 0 :(得分:16)
根据我的经验,当你有几个类需要响应相同的方法或方法时,最好使用接口,以便它们可以被其他代码互换使用,这些代码将针对这些类的通用接口编写。接口的最佳用途是协议很重要,但每个类的底层逻辑可能不同。如果您原本会复制逻辑,请考虑使用抽象类或标准类继承。
在回答问题的第一部分时,我建议不要为每个类创建一个界面。这会不必要地混乱你的类结构。如果您发现需要界面,可以随时添加。希望这有帮助!
亚当
答案 1 :(得分:6)
我也同意adamalex's response接口应该由应该响应某些方法的类共享。
如果类具有相似的功能,但在祖先关系中没有直接相关,那么接口将是将该功能添加到类而不在两者之间复制功能的好方法。 (或者有多个实现只有细微差别。)
虽然我们正在使用汽车类比,一个具体的例子。假设我们有以下类:
Car -> Ford -> Escape -> EscapeHybrid
Car -> Toyota -> Corolla -> CorollaHybrid
汽车有wheels
,可以Drive()
和Steer()
。所以这些方法应该存在于Car
类中。 (可能Car
类将是一个抽象类。)
走下去,我们得到了Ford
和Toyota
之间的区别(可能是因为车上标志类型的差异,也可能是一个抽象类。)
然后,最后我们有一个Escape
和Corolla
类,它们是完全实现为汽车的类。
现在,我们如何制作 Hybrid 车辆?
我们可以有一个Escape
的子类,EscapeHybrid
添加FordsHybridDrive()
方法,Corolla
的子类CorollaHybrid
加ToyotasHybridDrive()
1}}方法。这些方法基本上都是做同样的事情,但我们有不同的方法。 Yuck。好像我们可以做得更好。
假设混合使用HybridDrive()
方法。由于我们不希望最终有两种不同类型的混合(在完美的世界中),因此我们可以创建一个IHybrid
方法,其中HybridDrive()
方法。
所以,如果我们要制作EscapeHybrid
或CorollaHybrid
类,我们所要做的就是实施IHybrid
接口< /强>
对于一个真实世界的例子,我们来看看Java。可以将对象与另一个对象进行比较的类实现Comparable
接口。顾名思义,接口应该是可比较的类,因此名称为“Comparable”。
就感兴趣而言,car example的Interfaces课程中使用了Java Tutorial。
答案 2 :(得分:6)
You shouldn't implement any of those interfaces at all.
类继承描述对象是 (例如:它的身份)。这很好,但是大多数情况下对象 是什么,远不如对象那么重要。这就是接口的用武之地。
界面应描述对象做什么),或者它的作用。通过这个我的意思是它的行为,以及在给定行为的情况下有意义的操作集。
因此,良好的界面名称通常应为IDriveable
,IHasWheels
等形式。有时,描述此行为的最佳方式是引用一个众所周知的其他对象,因此您可以说“行为就像其中之一”(例如:IList
),但恕我直言,这种命名形式属于少数。< / p>
鉴于逻辑,接口继承有意义的场景与对象继承有意义的场景完全不同 - 通常这些场景与彼此都是。
希望能帮助您思考实际需要的接口: - )
答案 3 :(得分:2)
我只说为你需要引用的东西制作一个界面。您可能还有其他一些需要了解汽车的类别或功能,但是有多少东西需要了解福特?
答案 4 :(得分:2)
不要构建你不需要的东西。如果事实证明你需要接口,那么返回构建它们是一件很小的努力。
另外,在迂腐的一面,我希望你实际上并没有构建看起来像这种层次结构的东西。这不是继承应该用于什么。
答案 5 :(得分:2)
只有在需要该级别的功能时才创建它。
重新分解代码始终处于正在进行的过程中。
有些工具可以让您在必要时提取界面。 例如。 http://geekswithblogs.net/JaySmith/archive/2008/02/27/refactor-visual-studio-extract-interface.aspx
答案 6 :(得分:1)
制作ICar和其余所有(Make = Ford,Model = Mustang和stuff)作为实现接口的类的成员。
你可能想拥有你的福特课程,例如GM课程,并且如果你不想沿着检查Make == Whatever
的路线走下去,那么它们都会实现ICar以便使用多态,这取决于你的风格。
无论如何 - 在我看来,这些是汽车的属性,而不是相反 - 你只需要一个界面,因为方法很常见:刹车,加速等等。
福特可以做其他车不能做的事吗?我不这么认为。
答案 7 :(得分:0)
我会创建前两个级别,ICar和IFord,然后单独留下第二级,直到我需要第二级的接口。
答案 8 :(得分:0)
仔细考虑您的对象如何在您的问题域中相互交互,并考虑您是否需要具有多个特定抽象概念的实现。使用接口提供围绕其他对象与之交互的概念的契约。
在您的示例中,我建议福特可能是制造商,Mustang是制造商福特使用的ModelName值,因此您可能会更喜欢:
IVehichle - &gt; CarImpl,MotorbikeImpl - 有一个制造商有很多ModelNames
答案 9 :(得分:0)
在关于difference between interface and class的答案中,我解释说:
因此,如果您需要存储福特的不同子类的'福特'变量('价值'概念),请创建一个IFord。否则,在你真正需要它之前不要打扰。
这是一个标准:如果不满足,IFord可能没用 如果满足,则前面答案中公开的其他标准适用:如果福特具有比汽车更丰富的API,则IFord对于多态性目的是有用的。如果没有,ICar就足够了。
答案 10 :(得分:0)
在我看来,界面是一个工具来强制要求一个类实现某个签名,或者(我喜欢想到它)一个特定的“行为”对我来说,我认为如果我在我的开头的资本我onterface名称作为人称代词,我尝试命名我的接口,以便他们可以这样读... ICanFly,IKnowHowToPersistMyself IAmDisplayable等...所以在你的例子中,我不会创建一个接口镜像完整的公共签名任何特定类别。我会分析公共签名(行为),然后将成员分成更小的逻辑组(越小越好),如(使用你的例子)IMove,IUseFuel,ICarryPassengers,ISteerable,IAccelerate,IDepreciate等......然后申请那些与我系统中其他类相关的接口需要它们
答案 11 :(得分:0)
一般来说,考虑这个问题的最佳方式(以及OO中的许多问题)是考虑合同的概念。
合同被定义为两个(或多个)当事方之间的协议,规定了各方必须履行的具体义务;在程序中,这是一个类将提供的服务,以及为了获得服务而必须提供的类。接口声明了实现接口的任何类必须满足的契约。
考虑到这一点,您的问题在某种程度上取决于您使用的语言和您想要做的事情。
经过多年的OO(比如,天啊,30年),我通常会为每个合同编写一个接口,特别是在Java中,因为它使测试变得如此简单:如果我有一个类的接口,我几乎可以轻松地构建模拟对象。
答案 12 :(得分:0)
接口旨在成为通用公共API,用户将被限制使用此公共API。除非您希望用户使用IMustangGT
的特定于类型的方法,否则您可能希望将接口层次结构限制为ICar
和IExpensiveCar
。
答案 13 :(得分:-1)
仅从Interfaces和抽象类继承。
如果您有几个几乎相同的类,并且您需要实现大多数方法,请使用和Interface结合购买其他对象。
如果Mustang类如此不同,那么不仅要创建一个接口ICar,还要创建IMustang
因此,福特和野马等级可以从ICar 和 IMustang继承ICar,Mustang和MustangGT。
如果您实施福特班并且方法与野马相同,请从野马购买:
class Ford{
public function Foo(){
...
Mustang mustang = new Mustang();
return mustang.Foo();
}