面向对象编程中“接口”的定义是什么?
好的,我的一个朋友来回谈“接口”在编程方面的含义。
什么是“界面”的最佳描述。
对我来说界面是一个阶级的蓝图,这是最好的定义吗?
一个接口是开发中一个比较重载和混淆的术语。
它实际上是一个抽象和封装的概念。 对于给定的“框”,它声明该框的“input”和“输出”。 在软件世界中,这通常意味着可以在框中调用的操作(以及参数),在某些情况下,这些操作的返回types。
它没有做的是,它没有定义这些操作的语义是什么,尽pipe在声明附近(比如通过注释)logging它们是很平常的(也是非常好的做法),或者select好的命名约定。 尽pipe如此,我们不能保证这些意图会被遵守。
这是一个比喻:当电视机closures时,请看看电视机。 它的界面是它的button,各种插头和屏幕。 它的语义和行为是需要input(例如,电缆编程)并且具有输出(在屏幕上显示,声音等)。 但是,当您查看未插入的电视机时,则会将预期的语义投影到界面中。 对于你所知道的,电视在插入时可能会爆炸。然而,基于它的“界面”,你可以认为它不会喝咖啡,因为它没有水的摄入量。
在面向对象的编程中,一个接口通常定义一组具有该接口的类的实例可以响应的方法(或消息)。
更令人困惑的是,在一些语言中,像Java一样,有一个具有特定语言语义的实际接口。 例如,在Java中,它是一组方法声明,没有实现,但是一个接口也对应于一个types并服从各种打字规则。
在其他语言中,比如C ++,你没有接口。 一个类本身定义了方法,但是你可以把类的接口想象成非私有方法的声明。 由于C ++编译的方式,你可以在没有实际实现的情况下获得头文件,在那里你可以拥有类的“接口”。 您还可以使用纯虚函数等模拟具有抽象类的Java接口。
一个界面绝对不是一个class级的蓝图。 蓝图,一个定义就是“详细的行动计划”。 一个接口承诺没有任何行动! 混淆的根源是,在大多数语言中,如果你有一个定义了一组方法的接口types,实现它的类“重复”相同的方法(但提供了定义),所以接口看起来像一个框架或一个class级轮廓。
考虑以下情况:
当一个僵尸突然袭击你的时候,你正处在一个空荡荡的大房间中间。
你没有武器。
幸运的是,一个活着的人正站在房间的门口。
“快!” 你喊他。 “扔我东西,我可以打僵尸!”
现在考虑:
你没有指定(也不在意)你的朋友会select什么折腾;
…但没关系,只要:
-
这是可以扔的东西(他不能把你的沙发折腾)
-
这是你可以抓住的东西(让我们希望他不扔一个手里剑)
-
这是你可以用来抨击僵尸的大脑(这排除枕头等)
无论你拿到棒球棒还是锤子都没关系 –
只要它实现你的三个条件,你就很好。
把它们加起来:
当你写一个界面的时候,你基本上是在说:“我需要一些……”
接口是您应遵守或给予的合同,具体取决于您是实施者还是用户。
我不认为“蓝图”是一个好用的词。 蓝图告诉你如何build立一些东西。 一个界面特别避免告诉你如何build立一些东西。
一个接口定义了如何与一个类进行交互,即它支持哪些方法。
对我来说界面是一个阶级的蓝图,这是最好的定义吗?
没有。蓝图通常包括内部。 但是一个接口纯粹是关于一个类的外部可见的东西……或者更确切地说,是一个实现接口的类的家族。
接口由常量的方法和值的签名以及实现接口的类和使用接口的类之间的(通常是非正式的)“行为契约”组成。
从技术上讲,我会将接口描述为一组方法(方法,属性,访问器…词汇取决于您使用的语言)与对象进行交互。 如果一个对象支持/实现一个接口,那么你可以使用接口中指定的所有方法来与这个对象进行交互。
在语义上,一个接口也可以包含关于你可能做什么或不可以做什么的约定(例如,你可以调用方法的顺序),以及作为回报,你可以假定关于对象的状态远。
在编程中,一个接口定义了一个对象的行为,但实际上并不指定行为。 这是一个合同,可以保证某个class级可以做某些事情。
考虑这个C#代码在这里:
using System; public interface IGenerate { int Generate(); } // Dependencies public class KnownNumber : IGenerate { public int Generate() { return 5; } } public class SecretNumber : IGenerate { public int Generate() { return new Random().Next(0, 10); } } // What you care about class Game { public Game(IGenerate generator) { Console.WriteLine(generator.Generate()) } } new Game(new SecretNumber()); new Game(new KnownNumber());
游戏类需要一个秘密号码。 为了testing它,你想注入什么将被用作一个秘密的数字(这个原则被称为控制反转)。
游戏类希望对于实际创build随机数的内容“开放式”,因此它会在其构造函数中询问“任何具有生成方法的东西”。
首先,接口指定对象将提供什么操作。 它只是包含了它的外观,但没有给出实际的实现。 这只是方法的签名。 通常,在C#中,接口前缀为I.这些类现在实现了IGenerate接口。 这意味着编译器将确保它们都有一个方法,返回一个int并称为Generate
。 现在游戏被称为两个不同的对象,其中每个实现正确的接口。 其他类将在构build代码时产生错误。
在这里,我注意到你使用的蓝图比喻:
一个类通常被看作是一个对象的蓝图。 一个接口指定了一个类需要做的事情,所以人们可以争辩说,它确实只是一个类的蓝图,但是因为一个类不一定需要一个接口,所以我认为这个比喻正在打破。 将界面视为合同。 “签字”的class级将被法律要求(由编译警察执行),以遵守合同中的条款和条件。 这意味着它将不得不做,界面中指定的是什么。
这一切都是由于某些面向对象语言的静态types本质,如Java或C#的情况。 另一方面,在Python中,使用另一种机制:
import random # Dependencies class KnownNumber(object): def generate(self): return 5 class SecretNumber(object): def generate(self): return random.randint(0,10) # What you care about class SecretGame(object): def __init__(self, number_generator): number = number_generator.generate() print number
在这里,没有一个类实现一个接口。 Python并不关心这个,因为SecretGame
类只是试图调用传入的对象。如果对象有一个generate()方法,那么一切都很好。 如果它不:KAPUTT! 这个错误在编译时不会被看到,但是在运行时,所以可能在程序已经被部署和运行的时候。 在你接近之前,C#会通知你。
这种机制被使用的原因,天真地说,因为在OO语言中,自然而然的function不是一stream的公民。 正如你所看到的, KnownNumber
和SecretNumber
包含生成一个数字的函数。 一个根本不需要这些课程。 因此,在Python中,可以把它们扔掉,自己select函数:
# OO Approach SecretGame(SecretNumber()) SecretGame(KnownNumber()) # Functional Approach # Dependencies class SecretGame(object): def __init__(self, generate): number = generate() print number SecretGame(lambda: random.randint(0,10)) SecretGame(lambda: 5)
一个lambda只是一个函数,它被声明为“随着你走”。 一个委托在C#中是一样的:
class Game { public Game(Func<int> generate) { Console.WriteLine(generate()) } } new Game(() => 5); new Game(() => new Random().Next(0, 10));
后面的例子在Java中是不可能的,因为Java不能像其他两个(Python和C#)一样使用函数。 在那里,接口是你在av中用这种方式来expression行为的唯一方法。
就我个人而言,我看到一个像模板的界面。 如果一个接口包含方法foo()和bar()的定义,那么你知道每个使用这个接口的类都有方法foo()和bar()。
让我们考虑一个人(用户或对象)想要完成一些工作。 他会联系一个中间人(Interface),他将与公司签订合同(使用实现的类创build的真实世界对象)。 由他来定义哪些公司将会执行并给他结果。 每个公司都会以自己的方式来实施,但结果是一样的。 就像这个用户将使用单个界面完成其工作。 我认为Interface将作为系统中可见的一部分,只有很less的命令将由内部子系统内部定义。
一个接口将类中的操作从实现中分离出来。 因此,一些实现可以提供许多接口。
人们通常会把它形容为class级方法中必须提供的“契约”。
这绝对不是一个蓝图,因为这也决定了实施。 全class的定义可以说是一个蓝图。
接口定义了从它inheritance的类必须实现的内容。 通过这种方式,多个类可以从一个接口inheritance,并且因为这个缺点,你可以
- 确保接口的所有成员都在派生类中实现(即使它只是抛出一个exception)
- 从调用者中抽象出类本身(将一个类的实例转换为接口,并与其交互,而不需要知道实际的派生类是什么)
有关更多信息,请参阅http://msdn.microsoft.com/en-us/library/ms173156.aspx
在我看来,接口的含义比Java中通常与之相关的更广泛。 我将“接口”定义为一组可用的操作,它们具有一些通用的function,可以控制/监视模块。
在这个定义中,我试图覆盖两个程序接口,客户端是一些模块,以及人机界面(例如GUI)。
正如其他人所说,一个接口在input和输出方面总是有一些合同。 界面不承诺任何关于“如何”的操作; 它只保证结果的某些属性,给定当前状态,选定的操作及其参数。
如上所述,“合同”和“协议”的同义词是适当的。
该接口包含您可以期望由类公开的方法和属性。
所以如果一个类的Cheetos Bag
实现了Chip Bag
界面,你应该期待一个Cheetos Bag
的行为完全像其他的Chip Bag
。 (即暴露.attemptToOpenWithoutSpillingEverywhere()
方法等)
两个系统通信的边界。
接口是一些OO语言如何实现ad hoc多态性 。 临时多态只是具有不同types的相同名称的函数。
常规定义 – 接口是指定实现它的类需要实现的方法的合约。
界面的定义随着时间而改变。 你觉得Interface只有方法声明吗? 怎么样的静态最终variables和Java 5之后的默认定义呢?
由于带有多个inheritance的钻石问题,接口被引入Java,这就是他们实际打算做的事情。
接口是为了摆脱多重inheritance问题而创build的结构,可以有抽象方法,默认定义和静态最终variables。
界面是“什么工作”一种方法不“如何”。 这是一个合同。 就像在优先级队列中一样,你知道如何处理它,但是你不知道的底层实现。 课程同意不会改变现有操作的“效果”,而且客户同意严格按照其接口使用该类,并忽略任何实现细节。
Layman例如:想想拥有一台可以控制任何电视机的通用遥控器,无论是旧的电视机型还是使用LCD或等离子屏幕的电视机。 您按2,然后按5,然后按Enter,任何屏幕将显示频道25,即使根据底层技术发生的机制大不相同。