面向对象编程中“接口”的定义是什么

我的一个朋友反复讨论“接口”在编程中的意义。

对“接口”最好的描述是什么?

对我来说,接口是类的蓝图。这是最好的定义吗?

183963 次浏览

接口是一个您应该遵守或给予的契约,这取决于您是实现者还是用户。

我不认为“蓝图”是个好词。蓝图告诉你如何建造一些东西。接口特别避免告诉您如何构建某些东西。

接口定义了如何与类交互,即它支持哪些方法。

对我来说,接口是一个类的蓝图,这是最好的定义吗?

没有。蓝图通常包括内部构件。但是接口纯粹是关于类外部可见的东西... 或者更准确地说,是实现接口的一系列类。

接口包括方法的签名和常量的值,以及实现接口的类和使用接口的其他类之间的“行为契约”(通常是非正式的)。

接口将类上的操作与内部的实现分离开来,因此,一些实现可能会提供许多接口。

人们通常将其描述为一个“契约”,用于描述类的方法中必须具备的内容。

它绝对不是一个蓝图,因为它也将决定实现。完整的类定义可以说是一个蓝图。

接口是开发中比较重载和容易混淆的术语之一。

它实际上是一个抽象和封装的概念。对于给定的“框”,它 宣称的“输入”和“输出”的框。在软件世界中,这通常意味着可以在框中调用的操作(以及参数) ,在某些情况下还意味着这些操作的返回类型。

它没有做的是定义这些操作的语义,尽管在声明附近记录这些操作(例如,通过注释)或选择好的命名约定是常见的(也是非常好的实践)。然而,并不能保证这些意图会得到遵循。

这里有一个类比: 看看你的电视时,它是关着的。它的界面是按钮,各种插头和屏幕。它的语义和行为是接受输入(例如,有线电视编程)和输出(显示在屏幕上,声音等)。然而,当你看着一台没有插上电源的电视机时,你正在把你所期望的语义投射到一个界面中。你知道的,电视一插上电源就会爆炸。然而,基于它的“界面”,你可以假设它不会做任何咖啡,因为它没有水的摄入量。

在面向对象编程中,接口通常定义具有该接口的类的实例可以响应的一组方法(或消息)。

更加令人困惑的是,在一些语言中,比如 Java,有一个实际的接口具有特定于语言的语义。例如,在 Java 中,它是一组方法声明,没有实现,但是一个接口也对应于一个类型并遵守各种类型规则。

在其他语言中,比如 C + + ,没有接口。类本身定义方法,但您可以将类的接口看作是非私有方法的声明。由于 C + + 的编译方式,您可以获得头文件,在这些头文件中,您可以拥有类的“接口”,而不需要实际的实现。您还可以使用具有纯虚函数的抽象类来模拟 Java 接口,等等。

接口肯定不是类的蓝图。按照一种定义,蓝图就是“详细的行动计划”。接口对动作没有任何承诺!混淆的根源在于,在大多数语言中,如果你有一个定义一组方法的接口类型,实现它的类“重复”相同的方法(但提供定义) ,所以接口看起来像一个骨架或类的轮廓。

从技术上讲,我将把接口描述为与对象交互的一组方法(方法、属性、访问器... ... 词汇表取决于您使用的语言)。如果一个对象支持/实现一个接口,那么您可以使用接口中指定的所有方法与该对象交互。

从语义上讲,接口还可以包含关于您可以做什么或不可以做什么的约定(例如,您可以调用方法的顺序) ,以及关于您可以假设对象的状态的约定,作为回报,您可以假设您到目前为止的交互方式。

接口定义从其继承的类必须实现的内容。通过这种方式,多个类可以从一个接口继承,并且由于这种继承,您可以

  • 确保接口的所有成员都在派生类中实现(即使它只是抛出异常)
  • 将类本身从调用方抽象出来(将类的实例强制转换为接口,并与其交互,而不需要知道实际的派生类 IS)

更多信息,请看这个 http://msdn.microsoft.com/en-us/library/ms173156.aspx

在我看来,接口比 Java 中通常与之相关联的接口具有更广泛的意义。我将“接口”定义为一组具有某些通用功能的可用操作,允许控制/监视模块。

在这个定义中,我试图同时涵盖编程接口(其中客户机是某个模块)和人工接口(例如 GUI)。

正如其他人已经说过的,在输入和输出方面,接口背后总是有一些契约。该接口不承诺任何关于操作“如何”的内容; 它只保证给定当前状态、所选操作及其参数的结果的某些属性。

就我个人而言,我看到的界面类似于模板。如果一个接口包含方法 foo ()和 bar ()的定义,那么您就知道每个使用这个接口的类都有方法 foo ()和 bar ()。

如上所述,“契约”和“协议”的同义词是合适的。

接口包含可以期望由类公开的方法和属性。

因此,如果类 Cheetos Bag实现了 Chip Bag接口,那么您应该期望 Cheetos Bag的行为与其他 Chip Bag完全一样。(即公开 .attemptToOpenWithoutSpillingEverywhere()方法等)

考虑以下情况:

你正在一个空荡荡的大房间里,突然一个僵尸袭击了你。

你没有武器。

幸运的是,一个活生生的人类站在房间的门口。

“快!”你对他喊道,“扔点东西给我,我可以用来打僵尸!”

现在考虑一下:
你没有具体说明(你也不关心)你的朋友会选择扔的 什么;
不过没关系,只要..:

  • 这是 可以被扔掉的东西(他不能把你扔到沙发上)

  • 这是你可以抓住的东西(让我们希望他没有投掷手里剑)

  • 这是你可以用来砸烂僵尸脑袋的东西(这就排除了枕头之类的东西)

不管你拿的是棒球棍还是锤子
只要它能满足你的三个条件,你就没问题。

总结一下:

当你编写一个接口时,你基本上是在说: “我需要一些... ...”

让我们考虑一个人(用户或对象)想要完成一些工作。他将联系一个中间人(Interface) ,该中间人将与公司签订合同(使用实现类创建的真实世界对象)。几乎没有什么类型的工作将由他定义哪些公司将实施,并给他的结果。 每个公司都将以自己的方式实施这项工作,但结果将是相同的。像这个用户将得到它的工作使用一个单一的界面。 我认为 Interface 将作为系统的可见部分,只有很少的命令,这些命令将由实现的内部子系统在内部定义。

在编程中,接口定义对象将具有的行为,但它不会实际指定行为。这是一个契约,它将保证,某个类可以做某些事情。

考虑下面这段 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());

Game 类需要一个秘密号码。为了测试它,你需要注入一个将被用作秘密数字的东西(这个原则叫做控制反转)。

游戏类希望对什么会真正创建随机数保持“开放心态”,因此它会在其构造函数中询问“任何具有 Generate 方法的东西”。

首先,接口指定对象将提供哪些操作。它只包含它看起来的样子,但是没有给出实际的实现。这只是方法的签名。按照惯例,C # 接口的前缀是一个 I。 这些类现在实现了 IGenerate Interface。这意味着编译器将确保它们都有一个返回 int 且称为 Generate的方法。 这个游戏现在被称为两个不同的对象,每个对象都实现了正确的接口。其他类在生成代码时会产生错误。

在这里,我注意到了你使用的蓝图类比:

类通常被看作是对象的蓝图。接口指定了类需要做的事情,因此可以认为它确实只是类的一个蓝图,但是由于类不一定需要接口,我认为这个比喻正在被打破。把接口想象成一个契约。“签名”的类将被法律要求(由编译器警察强制执行) ,遵守合同中的条款和条件。这意味着它必须执行接口中指定的操作。

这都是由于某些面向对象语言的静态类型特性造成的,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类只会尝试调用传入的任何对象。如果对象具有 generated()方法,则一切正常。如果不行: KAPUTT! 这个错误不会在编译时出现,而是在运行时出现,所以可能在程序已经部署和运行时出现。C # 会在你接近它之前就通知你。

之所以使用这种机制,是因为在面向对象语言中,函数自然不是一等公民。正如你所看到的,KnownNumberSecretNumber只包含生成一个数字的函数。一个人根本不需要这些课程。因此,在 Python 中,人们可以抛弃它们,自己选择函数:

# 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 只是一个函数,它被声明为“ in line,as you go”。 委托在 C # 中是一样的:

class Game
{
public Game(Func<int> generate)
{
Console.WriteLine(generate())
}
}


new Game(() => 5);
new Game(() => new Random().Next(0, 10));

附注: 后面的例子在 Java7中是不可能的。在那里,接口是指定此行为的唯一方法。然而,Java8引入了 lambda 表达式,因此 C # 示例可以很容易地转换成 Java (Func<int>变成 java.util.function.IntSupplier=>变成 ->)。

两个系统通信的边界。

接口是一些面向对象语言实现 特设多态的方式。特设多态就是在不同类型上运行的具有相同名称的函数。

传统定义-接口是一个契约,它指定了需要由实现它的类实现的方法。

接口定义随着时间的推移发生了变化。你认为 Interface 只有方法声明吗?静态最终变量怎么样? Java5之后的默认定义怎么样。

接口之所以被引入 Java 是因为多重继承的 Diamond 问题,而这正是他们真正想要做的。

接口是为了摆脱多重继承问题而创建的结构,它可以包含抽象方法、默认定义和静态最终变量。

Http://www.quora.com/why-does-java-allow-static-final-variables-in-interfaces-when-they-are-only-intended-to-be-contracts

简而言之,接口试图解决的基本问题是将使用方式与实现方式分离开来。但是您应该考虑接口 不是合同。阅读更多 给你