好的,我的一个朋友来回探讨编程中“接口”的含义。
什么是“接口”的最佳描述。
对我来说,接口是类的蓝图,这是最好的定义吗?
好的,我的一个朋友来回探讨编程中“接口”的含义。
什么是“接口”的最佳描述。
对我来说,接口是类的蓝图,这是最好的定义吗?
Answers:
接口是开发中比较重载和令人困惑的术语之一。
它实际上是抽象和封装的概念。对于给定的“盒子”,它声明该盒子的“输入”和“输出”。在软件领域,这通常意味着可以在框上调用的操作(以及参数),在某些情况下,还指这些操作的返回类型。
尽管通常(在非常好的实践中)在声明附近(例如,通过注释)记录它们,或者选择良好的命名约定,但这并不能定义这些操作的语义。但是,不能保证将遵循这些意图。
打个比方:关掉电视看一下。它的界面是它具有的按钮,各种插头和屏幕。它的语义和行为是接受输入(例如,电缆编程)并具有输出(在屏幕上显示,声音等)。但是,当您查看未插入的电视时,您正在将期望的语义投影到界面中。就您所知,插入电视可能会爆炸。但是,根据其“界面”,您可以假定电视不会煮咖啡,因为它没有水。
在面向对象的编程中,接口通常定义具有该接口的类的实例可以响应的方法(或消息)集。
造成混淆的是,在某些语言(如Java)中,存在一个具有特定于语言的语义的实际接口。例如,在Java中,它是一组方法声明,没有实现,但是接口也对应于一种类型,并遵守各种键入规则。
在其他语言中,例如C ++,您没有接口。类本身定义了方法,但是您可以将类的接口视为非私有方法的声明。由于C ++的编译方式,您将获得头文件,而无需实际实现即可获得类的“接口”。您也可以使用带有纯虚函数等的抽象类来模仿Java接口。
接口绝对不是类的蓝图。根据一个定义,蓝图是“详细的行动计划”。接口对动作没有任何承诺!造成混淆的原因是,在大多数语言中,如果您有定义一组方法的接口类型,则实现该方法的类将“重复”相同的方法(但提供了定义),因此该接口看起来像骨架或课程大纲。
请考虑以下情况:
当僵尸突然袭击您时,您位于一个空旷的大房间中间。
你没有武器。
幸运的是,一个活着的人类正站在房间的门口。
“快!” 你对他大喊。“给我扔东西,我可以用它来打僵尸!”
现在考虑:
没有指定(你也不关心)究竟是什么你的朋友会选择折腾;
...但是没关系,只要:
这是可以扔的东西(他不能把沙发扔给你)
这是您可以抓住的东西(希望他不要扔手里剑)
您可以使用它来打击僵尸的大脑(排除枕头之类的东西)
不论是棒球棒还是锤子,都没关系-
只要能满足您的三个条件,您就很好。
把它们加起来:
编写界面时,您基本上是在说:“我需要……”
接口是您应该遵守或给予的合同,具体取决于您是实施者还是用户。
在“编程”中,接口定义对象将具有的行为,但实际上不会指定行为。这是一个合同,可以保证某个类可以做某事。
在这里考虑这段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());
游戏类需要一个秘密号码。为了测试它,您想注入将用作秘密数字的内容(此原理称为控制反转)。
游戏类希望对什么将实际创建随机数持开放态度,因此将在其构造函数中询问“具有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语言中,功能自然不是一流的公民。如您所见,KnownNumber
并且仅SecretNumber
包含生成数字的函数。一个人根本不需要所有的课程。因此,在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 7之前,后面的示例不可能像这样。在那里,接口是指定此行为的唯一方法。但是,Java 8引入了lambda表达式,因此C#示例可以非常容易地转换为Java(Func<int>
成为java.util.function.IntSupplier
和=>
成为->
)。
从技术上讲,我将接口描述为与对象进行交互的一组方式(方法,属性,访问器...词汇取决于您使用的语言)。如果对象支持/实现接口,则可以使用接口中指定的所有方式与该对象进行交互。
语义上,接口还可以包含关于您可能会做什么或可能不会做什么的约定(例如,您可以调用方法的顺序),以及关于给定的交互方式,您可以假定对象的状态的约定,因此远。
接口定义从其继承的类必须实现的内容。这样,多个类可以从一个接口继承,并且由于这种继承性,您可以
有关更多信息,请参见http://msdn.microsoft.com/zh-cn/library/ms173156.aspx
我认为,接口的含义比Java中通常与之相关的含义更广泛。我将“接口”定义为具有一些常用功能的一组可用操作,这些功能允许控制/监视模块。
在这个定义中,我尝试涵盖客户端是某个模块的程序化界面和人机界面(例如GUI)。
正如其他人已经说过的那样,就输入和输出而言,接口总是有一些契约。该界面不保证有关操作的“方式”。给定当前状态,所选操作及其参数,它只能保证结果的某些属性。
如上所述,“合同”和“协议”的同义词是合适的。
该接口包含您可以期望由类公开的方法和属性。
因此,如果类Cheetos Bag
实现了Chip Bag
接口,则您应该期望a Cheetos Bag
的行为与其他任何行为完全相同Chip Bag
。(即,公开.attemptToOpenWithoutSpillingEverywhere()
方法等)
常规定义-接口是一种约定,它指定实现它的类需要实现的方法。
接口的定义已随时间而改变。您是否认为Interface仅具有方法声明?静态最终变量如何?Java 5之后的默认定义如何?
接口是由于具有多个继承的Diamond问题而引入Java的,而这正是它们实际要做的。
接口是为避免多重继承问题而创建的结构,可以具有抽象方法,默认定义和静态最终变量。
接口是某些OO语言如何实现即席多态性。临时多态性只是具有相同名称且在不同类型上运行的函数。