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

时间:2010-05-19 15:34:18

标签: oop interface definition

好的,我的一个朋友在编程中对“界面”的含义进行反复讨论。

“界面”的最佳描述是什么。

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

16 个答案:

答案 0 :(得分:152)

接口是开发中更多过载和混乱的术语之一。

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

它没有做的是定义这些操作的语义是什么,尽管在声明附近(例如,通过注释)记录它们是常见的(并且非常好的做法),或者选择好的命名约定。然而,无法保证会遵循这些意图。

这是一个类比:看看电视何时关闭。它的界面是它有的按钮,各种插头和屏幕。它的语义和行为是它需要输入(例如,有线节目)并具有输出(在屏幕上显示,声音等)。但是,当您查看未插入的电视时,您将预期的语义投射到界面中。如你所知,当你插上电视时,电视可能会爆炸。但是,基于它的“界面”,你可以假设它不会制作任何咖啡,因为它没有水摄入量。

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

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

在其他语言中,如C ++,您没有接口。类本身定义了方法,但您可以将类的接口视为非私有方法的声明。由于C ++编译的方式,你可以得到头文件,你可以在没有实际实现的情况下拥有类的“接口”。您还可以使用纯虚函数等抽象类来模仿Java接口。

接口绝对不是课程的蓝图。根据一个定义,蓝图是“详细的行动计划”。接口承诺一个动作!混淆的原因在于,在大多数语言中,如果您有一个定义一组方法的接口类型,实现它的类“重复”相同的方法(但提供定义),因此接口看起来像一个骨架或一个班级大纲。

答案 1 :(得分:146)

考虑以下情况:

  

当一个僵尸突然攻击你时,你正处在一个空旷的大房间中间。

     

你没有武器。

     

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

     

“快!”你对他大喊大叫。 “把我的东西扔进僵尸吧!”

现在考虑:
您没有指定(也不关心)您的朋友选择抛弃的 ;

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

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

  • 你可以用它来抨击僵尸的大脑(排除枕头等)

无论你是拿棒球棒还是锤子都没关系 只要它实现了你的三个条件,你就会很好。

总结一下:

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

答案 2 :(得分:33)

接口是您应遵守或给予的合同,具体取决于您是实施者还是用户。

答案 3 :(得分:16)

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

接口定义了如何与类进行交互,即它支持的方法。

答案 4 :(得分:7)

  

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

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

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

答案 5 :(得分:5)

在编程中,接口定义了对象的行为,但实际上并不指定行为。这是一份合同,可以保证某个班级可以做某事。

在这里考虑这段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接口。这意味着编译器将确保它们都有一个方法,它返回一个int并被称为Generate。 游戏现在被称为两个不同的对象,每个对象都实现了正确的界面。其他类在构建代码时会产生错误。

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

一个类通常被视为对象的蓝图。一个接口指定了一个类需要做的事情,所以有人可能会说它确实只是一个类的蓝图,但由于一个类不一定需要一个接口,我认为这个比喻是破坏的。将接口视为合同。 “签署”的类别将在法律上要求(由编制警察强制执行),以遵守合同中的条款和条件。这意味着它必须做,界面中指定的内容。

这完全归功于某些OO语言的静态类型特性,就像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语言中,自然功能不是一等公民。如您所见,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只是一个函数,在你去的时候被宣布为“在行”。 代表在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。

中以这种方式表达行为的唯一方式

答案 6 :(得分:4)

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

从语义上讲,一个接口也可以包含关于你可能做什么或不做什么的约定(例如,你可以调用这些方法的顺序),以及关于对象的状态,你可以假设的内容你到目前为止互动了。

答案 7 :(得分:2)

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

答案 8 :(得分:2)

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

答案 9 :(得分:1)

接口将类上的操作与实现中的操作分开。因此,一些实现可以提供许多接口。

人们通常会将其描述为该类方法必须具备的“契约”。

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

答案 10 :(得分:1)

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

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

有关详细信息,请参阅此http://msdn.microsoft.com/en-us/library/ms173156.aspx

答案 11 :(得分:1)

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

在这个定义中,我尝试涵盖编程接口,其中客户端是某个模块,以及人机接口(例如GUI)。

正如其他人已经说过的那样,在输入和输出方面,接口总是有一些合同。界面不承诺任何关于操作的“方式”;在给定当前状态,所选操作及其参数的情况下,它仅保证结果的某些属性。

答案 12 :(得分:1)

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

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

因此,如果某个类Cheetos Bag实现了Chip Bag接口,那么您应该期望Cheetos Bag的行为与任何其他Chip Bag完全相同。 (也就是说,公开.attemptToOpenWithoutSpillingEverywhere()方法等)

答案 13 :(得分:1)

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

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

由于具有多重继承的Diamond问题,接口被引入Java,这就是他们实际打算做的事情。

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

http://www.quora.com/Why-does-Java-allow-static-final-variables-in-interfaces-when-they-are-only-intended-to-be-contracts

答案 14 :(得分:0)

A boundary across which two systems communicate.

接口是一些OO语言实现ad hoc polymorphism的方式。 Ad hoc多态性只是在不同类型上运行相同名称的函数。

答案 15 :(得分:0)

简而言之,接口要解决的基本问题是将我们如何使用某些东西与实现方式分开。但是您应该考虑使用接口is not a contract。了解更多here