-
09-06-2019 - |
题
当我应该用一个接口以及何时应使用一个基类?
它应该始终是一个接口,如果我不想要的实际定义的基执行方法?
如果我有一条狗和猫类。我为什么要实施IPet而不是PetBase?我可以理解具有接口ISheds或IBarks(IMakesNoise?), 因为那些可以放在一个宠物通过的宠物基础,但我不了解其使用对于一般的宠物。
解决方案
让我们把你的如一只狗和猫类,让我们说明使用C#:
这两个一个的狗和猫是动物,具体而言,四足动物哺乳动物(动物为主的太一般)。让我们假设你有一个抽象的类哺乳动物,对于它们两个:
public abstract class Mammal
这个基类可能会有缺省方法,如:
- 喂
- 伴侣
所有这些行为具有或多或少相同执行之间的任何一种。定义的这个,你将拥有:
public class Dog : Mammal
public class Cat : Mammal
现在让我们假设有其他哺乳动物,它们通常会看到在一个动物园:
public class Giraffe : Mammal
public class Rhinoceros : Mammal
public class Hippopotamus : Mammal
这将仍然是有效的,因为核心的功能 Feed()
和 Mate()
将仍然是相同的。
然而,长颈鹿,犀牛,河马,不完全的动物,你可以让宠物出来的。那是一个接口,将是有用的:
public interface IPettable
{
IList<Trick> Tricks{get; set;}
void Bathe();
void Train(Trick t);
}
执行上述合同将不会之间是相同的一只猫和狗;把他们实现在一个抽象的类继承的将是一个坏主意。
你的狗和猫的定义,现在应该是这样的:
public class Dog : Mammal, IPettable
public class Cat : Mammal, IPettable
理论上你可以复盖他们从一个较高的基类,但实质上一个接口,可以添加在只有你需要的东西变成一类,无需继承。
因此,因为通常你可以只能继承一个抽象的类(在大多数类型的静态OO种语言就是...例外情况包括C++)但是能够实现多个接口,它可以构建的对象在一个严格的 如需要 基础。
其他提示
好了,乔希*布洛赫所说的自己 有效的Java2d:
喜欢口超过抽象的类
一些要点:
现有的课程可以很容易地进行改造,以实现一个新的 接口.你所要做的是 所需要的方法如果他们还没 存在,并添加一个执行条款 这类声明。
接口的适用于定义的混入.严格地说, 混入类型的一类可以 实施此外,以其"主 类型"宣布它提供了 有些可选择的行为。例如, 可比性是一个混入接口 允许的一类宣布,它的 实例有关于 其他的相互比较的对象。
接口,允许建造的非等级类型 框架.类型等级结构 伟大组织的一些东西,但 其他的事情不属整齐地进入一个 刚层次结构。
接口使用安全的、强大的功能的增强 通过 总结每类成语。如果你使用 抽象的类定义的类型,你 留的程序员都想加入 功能没有选择,只 使用的继承。
此外,可以结合起来的美德 接口和抽象的类的 提供一个抽象的骨骼 实现类与每 平凡的接口,你的出口。
另一方面,接口都是在非常困难的发展。如果你加入的方法的一个接口,这会破坏这一切的实现。
PS:购买的这本书。这是一个更加详细。
现代风格的定义IPet 和 PetBase.
利用接口的其他代码可以使用它没有任何关系的任何其他可执行代码。完全"清洁"。 也接口,可以混合。
但基类的用于简单的实现和共同的事业。所以,提供一个抽象的基类以及为了节省时间和代码。
接口和基类代表两个不同形式的关系。
继承 (基类)表示"是"的关系。E.g。一只狗或猫"是的"宠物。这种关系始终代表(单) 目的 类(结合 "单一责任的原则").
接口, 另一方面,代表 额外的功能 一类。我会叫它"是"的关系,就像在"Foo
是一次性的",因此 IDisposable
接口。
接口
- 定义之间的合同2模块。不能有任何执行情况。
- 大多数语言,可以实现多个接口
- 修改的接口是一个重大更改。所有实现,需要重新编译/修改。
- 所有成员都是公开的。实现必须实现所有成员。
- 接口帮助脱钩。你可以使用模拟框架,以嘲笑任何背后有一个界面
- 接口通常表示一种行为
- 接口实现分隔/彼此隔离
基类
- 允许添加一些 默认的 执行,你得到免费的,通过推导
- 除了C++,你可以只从一个类。甚至如果可以从多个课程,这通常是一个糟糕的想法。
- 改变基类是相对比较容易。派生不需要做什么特别的东西
- 基类可以宣布保护和公共职能,可以由派生
- 抽象的基类不能嘲笑轻松像接口
- 基类通常表明类型的层级(一)
- 类推导可以来取决于一些基本的行为(具有复杂的知识的父母实现)。事情可能是混乱的,如果你改变基执行对一个人和打破其他人。
一般来说,应该有利于接口抽象的课程。原因之一使用一个抽象的类是如果你拥有共同实施之间的具体课程。当然,你还是应该宣布一个接口(IPet),并有一个抽象的类(PetBase)执行这一界面。使用小型、不同的接口,可以使用的倍数,以进一步提高灵活性。接口,允许最大的灵活性和可移植性的种类型跨越边界。经过时引用跨越边界,一直通过接口并不具体类型。这使得接收端,以确定具体执行并提供最大的灵活性。这绝对是真实的,当编制一个双工/BDD时尚。
该团伙的四个说在他们的书",因为继承公开一个子类细节的其父母的执行情况,它们常常说,'继承破坏了封装".我相信这是真实的。
这是很漂亮。净具体,但是该框架的设计准则的书认为,一般课程得到更多的灵活性,在一个不断发展的框架。一旦一个接口装运的,你不会有机会改变它不断代码,用于接口。与一个类然而,可以修改它并不断代码链接。只要你做出正确的修改,其中包括增加新的功能,你将能够扩大和发展你的代码。
克日什托夫*Cwalina说,关于第81页:
过去的三个版本的.净的框架,我已经谈到了这一准则有相当多的开发人员在我们的团队。他们中的许多人,包括那些最初不同意该准则,必须说,他们感到遗憾的是具有装运的一些API为一个接口。我没有听说过即使一种情况下,有人遗憾地表示,它们运一类。
这就是说肯定有一个地方的对接口。作为一般指导原则总是提供一个抽象的基类执行的一个接口,如果没有人作为示例的方式来实现接口。在最好的情况下,基类将会节省大量的工作。
胡安,
我喜欢思考的接口,作为一种特征的一类。一个特别的狗的品种类,说YorkshireTerrier,可以以后裔的母狗类,但它也是实现IFurry,IStubby,并IYippieDog.所以这类定义是什么类但接口告诉我们的事情。
这样做的优势是,它使我可以,例如,收集所有IYippieDog的扔进我的海洋集合。所以现在我可以跨越一个特定的对象和找到那些满足条件我看没有检查这类太密切。
我发现接口,真的应该定义的一个子集的公共行为的一类。如果定义的所有公共行为的所有类实施,然后它通常并不需要存在。他们不告诉我任何有用的。
这个想法虽然去柜台的想法,每类都应有一个界面,并应代码的接口。这很好,但是你结束了一个很大的一对一的接口,以类和它使事情变得混乱。我的理解是,这个想法是不真的成本是什么,现在你可以交换的东西和出提供方便。然而,我发现,我很少这样做。大多数时间我只是修改现有的类在地方和具有完全相同的问题,我始终没有如果公共口的这类需求的变化,但我现在必须改变它在两个的地方。
所以如果你认为像我这样的你肯定会说这只猫和狗是IPettable.这是一种特征描述相匹配他们两个。
其他的一块这个但是他们应该有相同的基础课吗?问题是他们需要被广泛地视为同一件事。当然,他们都是动物,但不适合我们如何去使用它们合在一起。
说我想要收集所有的动物类别,并把它们放在我的柜容器。
或者他们需要哺乳动物吗?也许我们需要某种形式的跨动物挤奶厂?
他们甚至需要联系在一起吗?它是不够的,只是知道他们都是IPettable?
我经常感受到的愿望得出一整类层次的时候我真的只是需要一类。我做它在期待有一天我可能会需要它并且通常我从来没有做。即使我做的,我通常会发现我已经做了很多修复它。那是因为第一类,我在创建不是狗,我不是那个幸运的,而不是鸭嘴兽。现在我的整个类层次结构是基于奇怪的情况下和我有很多浪费了代码。
你可能还会发现在某一点,并不是所有的猫是IPettable(喜欢这样无毛的一个)。现在你可以移动,接到的所有衍生物类别合适的。你会发现一个更断变化,突然猫都不再来自PettableBase.
这里是基本的和简单的等复杂的接口和基类:
- 基类=目的继承权。
- 接口=功能继承。
欢呼
我建议使用组成,而不是继承,只要有可能。使用的接口,但使用成员对象为基础的实施。这样,你可以定义的一个工厂,建造你的目的的行为在某种方式。如果你想改变行为,那么你做一个新的工厂的方法(或抽象的工厂),创造不同类型的子的对象。
在某些情况下,你可能会发现,你的主要对象不需要接口,如果所有的可变行为定义中的帮助对象。
因此,不是IPet或PetBase,你可能会结束了一个宠物,它有一个IFurBehavior参数。该IFurBehavior参数设置的CreateDog()方法PetFactory.它为此参数这就是所谓的棚()方法。
如果你这样做你就会找到你的代码的更为灵活和最简单的对象,处理非常基本的系统范围的行为。
我建议这种模式,即使在多继承的语言。
解释以及在这 Java世界的文章
我个人更倾向于使用的接口定义的接口-即部分系统的设计,指定如何东西应该进行访问。
这并不罕见,我将有一个类执行1或多个接口。
抽象的I类使用作为基础别的东西。
以下是摘自上述提及的文章 JavaWorld.com 文章,作者托尼*Sintes,04/20/01
接口与抽象的类
选择接口和抽象的课程不是一个无论是/或者主张。如果你需要改变你的设计,使它成为一个接口。然而,可能有抽象的类提供一些默认的行为。抽象的课程都是优秀的候选人内部的应用程序框架。
抽象的课让你定义的某些行为;他们迫使你的子类提供其他人。例如,如果你有一个应用程序框架内,一个抽象的类可以提供默认的服务,例如事件和消息处理。这些服务允许您应用程序插入到应用程序框架。然而,有一些应用程序特有的功能,只有应用程序可以执行。这种功能可能包括启动和关闭的任务,这常常是应用程序的依赖性。因此,而不是试图定义,行为本身的抽象基类可以声明摘要关闭和启动的方法。基类知道,它需要这些方法,但是一个抽象的类可以让你的类承认,它不知道如何执行这些行动;只知道它必须发起的行动。当是时候启动的,抽象的级可以调用该启动的方法。当基类调用这种方法,Java调用定义的方法通过的子类。
许多开发忘记这一类定义的一个抽象的方法可以调用该方法。抽象的类是一个很好的方式来创建计划的继承的层次结构。他们也是一个不错的选择非叶类在类层次结构。
类比.接口
有人说你应该定义中的所有类条款的接口,但是我想建议似乎有点极端。我使用的接口,当我看到那东西在我的设计将经常发生变化。
例如,《战略》的模式可以让你换新的算法和进程进入你的节目没有改变的对象,使用它们。媒体播放器可能知道怎么玩Cd Mp3和果文件。当然,你不要硬编码那些播放算法进入播放器;这将使它难以增加新的格式,如AVI。此外,你代码将充斥着无用的情况下发言。和雪上加霜的是,你会需要更新这些情况的发言,每次增加一个新的算法。所有在所有,这不是一个非常面向对象的方法的程序。
与《战略》的模式,可以简单地封算法背后的一个对象。如果你这样做,你可以提供新的媒体插件在任何时间。让我们打电话的插件类MediaStrategy.这一目将有一个方法:playStream(流s)。所以添加一个新的算法,我们只是简单地延长我们的算法类。现在,当程序遇到的新型媒体,它只是代表们在演奏的流到我们的媒体战略。当然,你需要一些管道到适当实例的算法战略将需要。
这是一个很好的地方使用一个接口。我们已经使用了战略模式,这清楚地表明一个地方在设计这会改变。因此,应该定义的战略作为一个接口。你应该一般有利于接口在继承时候你想一个对象有某些类型;在这种情况下,MediaStrategy.依靠继承类型身份是危险的;它锁定你成为一个特定的继承层次结构。Java不允许多个继承权,所以你不能延长的东西,让你一个有用的执行或更多类型的身份。
也记不得到冲走OO(看看博客)和总是模型基于对象的行为必需的,如果你是设计一个应用程序,唯一的行为你需要的是一个通用名称和种类对于一个动物,然后你只需要一流的动物与酒店的名称,而不是数以百万计的课程,为每个可能的动物世界。
我有一个粗略的规则的拇指
功能: 可能是不同的所有部分:接口。
数据,并功能,部分将主要是一样的,部分不同: 抽象的类。
数据,并功能、实际工作,如果延长仅略有变化: 普通(具体的)类
数据和功能,没有更改计划: 普通(具体的)类与最终改性剂。
数据,也许功能:只读: 枚举的成员。
这是非常粗略和现成的,而不是在所有严格的定义,但是有一个频谱的接口,这里的一切的目的是要改变,以枚举的这里的一切都是固定的,有点像一只读文件。
接口应该较小。真的很小。如果你真的打破你的对象,那么你的接口将很可能只有含有一些非常具体的方法和性质。
抽象的课程是捷径。有没有事情,所有衍生物的PetBase分享,你可以代码一旦做?如果是的话,那么它的时间为一个抽象的类。
抽象的类也有限制性。虽然他们给你一个伟大的快捷方式生产子对象,任何给定的对象只能实现一个抽象的类。很多时候,我找到这个限制的摘要类别,这就是为什么我用的许多接口。
抽象的类可能包含多个接口。你PetBase抽象的类可以实现IPet(宠物有所有者)和IDigestion(宠物吃,或至少他们应该)。然而,PetBase可能不会实现IMammal,因为不是所有的宠物都是哺乳动物,并不是所有的哺乳动物的宠物。你可以添加一个MammalPetBase延伸PetBase和加IMammal.只有鱼可以PetBase和加IFish.IFish会ISwim和IUnderwaterBreather为的接口。
是的,我的例子是广泛的过于复杂为简单的例子,但这是部分伟大的事情有关如何接口和抽象的类一起工作。
这种情况下,对基类过接口是解释以及在Submain.净编码指南:
基类与接口 一个接口类型是一部分 说明的一种价值,能 支持由许多类型的对象.使用 基类,而不是接口 只要有可能。从一个版本 看,课程更加灵活 于接口。有一个班,你可以 船1.0版和再版本 2.0添加一个新的方法。只要该方法不是抽象的, 现有的任何源类继续 能不变。
因为接口,不支持 实施继承权, 模式适用于类做 不适用于接口。增加一个 方法一个接口相等 增加一个抽象的方法的基 类;任何类实现了 接口将突破,因为该类 不执行的新方法。接口适合的 以下情况:
- 几个不相关的课程需要支持的协议。
- 这些课程已经建立了基类(对 例, 有些用户接口(UI)的控制, 和一些XML网页服务)。
- 聚合是不合适或不可行的。在所有其他的 情况, 类继承的是一个更好的模型。
来源: http://jasonroell.com/2014/12/09/interfaces-vs-abstract-classes-what-should-you-use/
C#是个精彩的语言这已经成熟和演变过去14年。这是为我们伟大的开发,因为一个成熟的语言为我们提供了一种多语言特点,是在我们的处置。
然而,与多的权力变得多的责任。这些特征中的一些可能被滥用,或者有时很难理解为什么你会选择使用的一个特征。多年来,一个特征,我已经看到许多开发人员的斗争是时候选择使用一个接口或选择使用的一个抽象的类。两者都有优点和缺点,并正确的时间和地点使用的每一个。但如何我们决定???
两者都提供重复使用的通用功能之间的类型。最明显的差异是接口提供不执行对他们的功能而抽象的类可用来实施一些"基地"或"默认"的行为,然后有能力"替代"这一默认行为的类源类型,如果有必要的。
这是都好和好并提供了很大的重复利用的代码和坚持干燥(不要重复自己)的原则的软件发展。抽象类伟大的使用,当你有一个"一"的关系。
例如:一个黄金猎犬"是"种类型的狗。那是一个贵宾。他们都可以树皮,作为所有的狗可以。但是,你可能想状态,贵宾狗公园是显着不同于"默认"狗吠叫。为此,它可能使你感觉到实现的东西如下:
public abstract class Dog
{
public virtual void Bark()
{
Console.WriteLine("Base Class implementation of Bark");
}
}
public class GoldenRetriever : Dog
{
// the Bark method is inherited from the Dog class
}
public class Poodle : Dog
{
// here we are overriding the base functionality of Bark with our new implementation
// specific to the Poodle class
public override void Bark()
{
Console.WriteLine("Poodle's implementation of Bark");
}
}
// Add a list of dogs to a collection and call the bark method.
void Main()
{
var poodle = new Poodle();
var goldenRetriever = new GoldenRetriever();
var dogs = new List<Dog>();
dogs.Add(poodle);
dogs.Add(goldenRetriever);
foreach (var dog in dogs)
{
dog.Bark();
}
}
// Output will be:
// Poodle's implementation of Bark
// Base Class implementation of Bark
//
正如你可以看到,这将是一个伟大的方式,让你干代码,并允许基类执行被称为当的任何类型可以仅仅依赖于默认的树皮,而不是一种特殊的情况下实施的。类似GoldenRetriever、拳击手、实验室可能都可以继承的"默认"(鲈鱼类)的树皮不收取任何费用,只是因为他们实施的狗抽象的类。
但我敢肯定,你已经知道了。
你在这里是因为你想要了解为什么你可能会想要选择一个接口超过一个抽象的类或反之亦然。好的一个原因,你可能想要选择一个接口超过一个抽象的类是,当你不具有或者希望防止违约的执行。这通常是因为种类,这是实现接口相关的不在一个"一"的关系。实际上,他们不必是相关的,在所有除事实上,每个类型的"能够"或有"能力"来做什么或有什么东西。
现在什么意思?好了,例如:一个人不是一只鸭子...和一只鸭子不是人类。很明显的。但是,这两个鸭子,一个人必须"能力"游泳(鉴于该人过去了他的游泳课程在第1年级)).此外,由于鸭子不是一个人或反之亦然,这不是一个"是"realationship,而是一个"能"的关系,我们可以使用一个接口,以说明:
// Create ISwimable interface
public interface ISwimable
{
public void Swim();
}
// Have Human implement ISwimable Interface
public class Human : ISwimable
public void Swim()
{
//Human's implementation of Swim
Console.WriteLine("I'm a human swimming!");
}
// Have Duck implement ISwimable interface
public class Duck: ISwimable
{
public void Swim()
{
// Duck's implementation of Swim
Console.WriteLine("Quack! Quack! I'm a Duck swimming!")
}
}
//Now they can both be used in places where you just need an object that has the ability "to swim"
public void ShowHowYouSwim(ISwimable somethingThatCanSwim)
{
somethingThatCanSwim.Swim();
}
public void Main()
{
var human = new Human();
var duck = new Duck();
var listOfThingsThatCanSwim = new List<ISwimable>();
listOfThingsThatCanSwim.Add(duck);
listOfThingsThatCanSwim.Add(human);
foreach (var something in listOfThingsThatCanSwim)
{
ShowHowYouSwim(something);
}
}
// So at runtime the correct implementation of something.Swim() will be called
// Output:
// Quack! Quack! I'm a Duck swimming!
// I'm a human swimming!
使用的接口,如上述代码会让你传递一个对象为一种"能"做到的事情。代码并不关心如何,它不它...它所知道的是,它可以叫游泳的方法上,对象和目将知道哪些行为采取在运行时根据其类型。
再次,这有助于你的码保持干燥,这样你就不必编写的多种方法,都呼吁的对象为预制件相同的核心功能(ShowHowHumanSwims(人),ShowHowDuckSwims(鸭),等等。)
使用一个接口,这使得调的方法不需要担心什么类型是其上或如何行为是实现的。它只知道,鉴于接口,每个对象必须拥有执行的游泳的方法,因此它是安全的呼吁它在其自己的代码,并允许的行为的游泳的方法处理在其自己的类。
摘要:
所以我主要的经验法则是,使用一个抽象的级的时候你希望实现一种"默认"功能类阶层或/和课程或种类型,你的工作与分享"是一个"关系(ex。狮子狗"是"种类型的狗).
另一方面使用一个接口的时候你没有一个"一"的关系,但有类型分享"的能力"来做到的东西或者有的东西(ex。鸭子"不是"人类。然而,鸭与人分享"的能力"游泳).
另一个差别注意到之间的抽象的课程和接口是这类可以实现一个对许多接口,但一类只能继承一个抽象的级(或任何类物质)。是的,你可以窝类,并有一个继承层次结构(其中许多程序做和应该有),但是你不能承受两类中的一个源类定义(这一规则适用于C#。在其他一些语言都能够做到这一点,通常仅仅因为缺少接口中这些语言)。
还记得当使用的接口,坚持的接口隔离的原则(ISP)。ISP国家没有客户应被迫依赖的方法,它不会使用。为此原因接口应该集中于具体的任务和通常都非常小(ex。IDisposable型).
另一个提示是如果你都是发展小型、简洁位的功能,使用的接口。如果你正在设计的大单元的功能,使用一个抽象的类。
希望这将清除的事情了一些人们!
还有如果你能想到的任何更好的实例,或想到点东西,请在下面的意见!
一个重要的区别是,你只能继承 一个 基类,但可以实现 很多 接口。所以你只要用一个基类如果你是 绝对肯定 你不需要也继承了一个不同的基类。此外,如果您找到您的接口越来越大,那么你应该开始找到它分解成几个逻辑片,定义独立的功能,由于没有规定这类不能实现他们所有的(或者你可以界定不同的界面,就会继承他们的所有组)。
当我第一次开始学习了解面向对象的节目,我做了方便和可能常见的错误的使用继承分享共同的行为-即使该行为不是必不可少的性质的对象。
为进一步建立一个例如用于这一特定问题,还有 很多 东西都petable-女朋友,汽车、模糊的毯子...-所以我可能有一个Petable类提供这种共同的行为,以及各种类继承。
然而,正在petable是不自然的一部分的任何这些物体。有很多重要的概念, 都 必须向他们的天性-女朋友是一个人,汽车是一个陆地车辆,猫哺乳动物...
行为应分配给第一个接口(包括默认接口的类),并提升为一个基类只如果他们是(a)共用一个大集团的类子集的一个大类-在同一意义上的"猫"和"人"均分"哺乳动物".
美中不足的是,在你明白的面向对象的充分的设计更好比我在第一次,你通常会做这种自动甚至没有考虑它。这样赤裸的真相的声明"代码的接口,不是一个抽象的类"变得如此显而易见的你有一个很难相信有任何人会打扰说它并开始试图阅读其他的含义进入它。
另一件事情我想补充的是,如果一类是 纯粹的 抽象-没有非抽象,非继承的成员或方法暴露儿童、父母、或客户机-那么为什么它是一类的?它可以被替换,在某些情况下通过一个接口,并在其他情况下通过空。
喜欢口超过抽象的类
理由, 主要考虑的要点[两个已经在这里提到]:
- 接口更加灵活,因为一类可以实现多 接口。由于Java没有多个继承、使用 抽象的课程,防止用户使用的任何其他类 层级结构。 在一般情况下,愿意接口的时候有没有默认 实现或状态。 Java集提供良好的例子 此(地图、设置,等等)。
- 抽象的类具有的优点是允许更好的前进 兼容性。一旦客户使用一个接口,你不能改变它;如果他们用一个抽象的级的,仍然可以添加行为没有 打破现有的代码。 如果兼容性是一个问题,考虑使用 抽象的课程。
- 甚至如果你做的已经默认的实现或内部的状态,
考虑提供一个界面与一个抽象的实施情况.这将协助客户,但仍然允许他们有更大的自由,如果
所需的[1].
当然,问题已经讨论的长度 在其他地方的[2,3,].
[1]它增加了更多的代码,当然,但是,如果简是你的主要问题,你也许应该避免在第一个地方!
[2]约书亚布洛克,有效Java、项目16-18.
以前的评论关于使用抽象的类共同的实现是绝对的标记。一个好处我还没有看到提及的是,使用的接口,使它更加容易执行的模拟对象为目的的单元的测试。定义IPet和PetBase为Jason Cohen说明使你能够模拟不同的数据的条件下容易,没有开销的物理数据库(直到你决定它的时间来测试真实的东西).
不要使用一个基类的除非你知道这意味着什么,并说明它适用于这种情况。如果适用,使用它,否则,使用的接口。但是注意到回答关于小型接口。
公共继承过度使用在面向对象设计并表示更多的大多数开发人员实现或正愿意实现。看看 里氏Substitutablity原则
总之,如果"是"B后,一个需要不超过B和提供不低于B,对每种方法公开。
从概念上讲,一个接口是用于正式和半正式定义设定的方法,一个对象提供。正式指定的方法姓名和签名,半正式意味着人可读文件相关的那些方法。接口只说明的API(后,API代表应用程序员 接口),他们不能含有的任何执行,这是不可能使用或运行的一个接口。他们只有作出明确的合同的,你应该如何互动的对象。
类提供一个执行情况,他们可以声明,他们实行零,一个或多个接口。如果一类的目的是要继承,该公约是前缀类名称与"基地"。
之间是有区别的基类和一个抽象的基类(ABC)。Abc混口和执行在一起。抽象的外部计算机编程装置的"摘要",这是"抽象==接口"。一个抽象的基类然后可以描述一个接口,以及一个空白,部分或完全执行,旨在继承。
意见时使用的接口与抽象的基类与刚类要变化很大,根据你的发展,以及哪种语言的发展。接口往往是相关联的只有静态类型的语言,例如Java或C#,但是动态型的语言也可以有接口和抽象的基础课程。在Python例如,这种区分是由清楚之间的一类,其中宣布它 实现了 一个接口,并对象,这是一个实例中的一类,而是说 提供 该接口。这有可能在动态的语言,两个对象是两个实例中的同一类,可以声明,他们提供完全 不同的 接口。在蟒蛇这是唯一可能的对象的属性,同时方法是共同国家之间的所有对象的一类。然而,在红宝石,对象可以有每个实例的方法,因此它是可能的接口之间的两个对象的同类可能变化作为程序员的愿望(然而,红宝石没有任何明确的方式的声明的接口).
在动态的语言接口对象往往是隐含地假定,无论是通过检查对象,并要求它什么方法,它提供了(三思而后行)或最好通过只是尝试使用所需的接口上的对象和醒目的例外情况,如果对象不提供这种界面(更容易请求宽恕要比许可)。这可能导致"误报",其中两个接口相同的方法的名称,但在语义上是不同的,但是交易这是你的代码的更多的灵活因为你不需要在指定前预见到所有可能用你的代码。
另一种选择,要牢记的是,使用"拥有"关系,又名"是实现条款"或"组合物"。 有时候,这是一个更清洁、更灵活的方式来结构的东西比使用"-一个"继承。
它可能不使尽可能多的意义,逻辑上说,只狗和猫既有"有"宠物,但它避免了共同的多个继承陷阱:
public class Pet
{
void Bathe();
void Train(Trick t);
}
public class Dog
{
private Pet pet;
public void Bathe() { pet.Bathe(); }
public void Train(Trick t) { pet.Train(t); }
}
public class Cat
{
private Pet pet;
public void Bathe() { pet.Bathe(); }
public void Train(Trick t) { pet.Train(t); }
}
是的,这个例子显示,有很多代码的工作重复和缺乏优雅的参与做的事情,这种方式。但人们也应当理解,这有助于保持狗和猫脱离的宠物类(在狗和猫没有访问专员的宠物),并将它留有狗和猫的继承的东西-可能的哺乳动物类。
组成是最好的时候没有任何私人访问是必要的,你不需要参照猫和狗的使用通用的宠物引用/指针。接口,给你的通用参考能力和可以帮助减少的详细程度代码,但他们也可以模糊的东西,当他们是很差的组织。继承权是有用的,当你需要的私人会员的访问,并在使用它,你们犯自己的高度联你的狗和猫类你的宠物类,这是一个陡峭的成本支付。
之间的继承权、组成和接口,没有一种方法是总是正确的,它有助于考虑如何在所有三个选项可用于和谐。三、继承通常的选择,应使用最频繁。
这取决于你的需求。如果IPet是很简单我宁愿来实现。否则,如果PetBase实现了一吨的功能你不希望重复,然后在。
的缺点实施一个基类是要求 override
(或 new
)现有的方法。这使得他们虚拟方法,这意味着你必须小心关于如何使用对象的实例。
最后,单一的继承权。净杀死我。一个天真的例子:说你正在做一个用户的控制,所以你会继承 UserControl
.但是,现在你被锁还继承 PetBase
.这迫使你重新组织,例如做一个 PetBase
类成员,代替。
@Joel:一些语言(例如C++)允许多个继承。
我通常不实施任,直到我需要的一种。我赞成接口抽象的班级,因为这给多一点的灵活性。如果有共同行为的一些继承类我动起来,使一个抽象的基类。我看不需要这两者,因为他们基本上服务器的目的相同,并且具有两者是一个糟糕代码闻(恕我直言),该方案已经过设计的。
关于C#在某种意义上的接口和抽象的类可以互换。但是,差别是:i)接口不能实现码;ii),因为这个接口不能呼叫进一步堆类;和iii)只能抽象的类可能继承一级,而多个接口,可以实现一类。
通过清、界面提供了一个层与其它代码。所有公共性质和方法的一类是默认情况下实现的隐含的接口。我们还可以定义的接口,作为一作用,当时没有任何类需要发挥这一作用,它已经实现它赋予它不同形式的执行取决于类执行。因此当你谈论的界面,你是说的多,当你正在谈论的基类,你们是在谈论继承权。两个概念的哎呀!!!
我发现一种模式的界面>Abstract>具体的工作在以下的使用情况:
1. You have a general interface (eg IPet)
2. You have a implementation that is less general (eg Mammal)
3. You have many concrete members (eg Cat, Dog, Ape)
这类抽象的定义默认的共同属性的具体课程,但强制执行的接口。例如:
public interface IPet{
public boolean hasHair();
public boolean walksUprights();
public boolean hasNipples();
}
现在,由于所有的哺乳动物有的头发和乳头(据我所知,我不是一个动物学家),我们可以滚到抽象的基类
public abstract class Mammal() implements IPet{
@override
public walksUpright(){
throw new NotSupportedException("Walks Upright not implemented");
}
@override
public hasNipples(){return true}
@override
public hasHair(){return true}
然后具体类仅仅是定义,他们直立行走。
public class Ape extends Mammal(){
@override
public walksUpright(return true)
}
public class Catextends Mammal(){
@override
public walksUpright(return false)
}
这种设计是很好的时候有很多的具体课程,并且你不想要维护样板,只是为程序一个接口。如果新的方法加入该接口,这会破坏所有的所得类,所以你仍然得到优点接口的办法。
在这种情况下,可能只是抽象以及能混凝土;然而,对抽象的指定有助于强调,这种模式正在被采用。
一个继承人的基类应该有"一个"关系。接口,代表"实现一个"关系。所以只能使用一个基础课的时候你的继承者维持是一个关系。