language-agnostic - "程序"的接口是什么意思?

  显示原文与译文双语对照的内容

我看到过几次,但我对它的含义并不完全清楚。 什么时候和为什么要这样做?

用他们correctly,我知道是哪些界面做了,但事实上我不清楚地了解这个使我思考我是没拿 on.

如果你是这样做的:


IInterface classRef = new ObjectWhatever()

你可以使用任何实现 IInterface的类? 你什么时候需要这样做? 于它的相关实现我唯一能想到的是如果你有一个方法,并且你不确定什么样的对象将被传递 expect. 你可能需要这么多的时间。 对象,该对象实现中的一个 interface, ( 另外,你怎么能写一个方法,使用? 这可能?

抱歉,如果我完全忘记了这点。


其他问题:

  • 使用接口命中性能?
  • 如果有的话?
  • 如果不需要维护两个代码位,你如何避免它?
时间:

关于接口和松散耦合代码的各种细节,控制反转等等问题,这里有一些不错的答案。 于理解为什么一个接口是useful,相关还有一些非常令人陶醉的讨论,所以我想借此机会把事物水位已经下降了

当我第一次接触接口时,我也很困惑它们的相关性。 我不知道为什么你需要他们。 如果我们使用的是语言( 如Java或者已经有了继承和我已经查看接口作为一个弱的继承形式和从某种意义上来说我是对你能想到的接口作为类似一个弱的继承形式但除此以外,我终于理解思考它们意味着的划分方法,探讨它的作为一种语言构成的共同特征或者行为,它被展出许多non-related由具有潜在的对象类中。

例如--假设你有一个模拟游戏,并且有以下类:


 class HouseFly inherits Insect {
 void FlyAroundYourHead();
 void LandOnThings();
 }

 class Telemarketer inherits Person {
 void CallDuringDinner();
 void ContinueTalkingWhenYouSayNo();
 }

显然,这两个对象在直接继承方面没有共同之处。 但是,你可以说他们都很烦人。

让我们假设我们的游戏需要某种随机的游戏,这会让游戏玩家在吃饭时感到厌烦。 这可能是家蝇或者Telemarketer或者两者都是 --,但你如何让两者都有一个函数? 你如何以同样的方式询问不同类型的对象?

关键是Telemarketer和家蝇都有共同的松散解释的行为,尽管它们在建模方面没什么相似之处。 因此,让我们创建一个既可以实现的接口:


 interface IPest {
 void BeAnnoying();
 }

 class HouseFly inherits Insect implements IPest {
 void FlyAroundYourHead();
 void LandOnThings();

 void BeAnnoying() {
 FlyAroundYourHead();
 LandOnThings();
 }
 }

 class Telemarketer inherits Person implements IPest {
 void CallDuringDinner();
 void ContinueTalkingWhenYouSayNo();

 void BeAnnoying() {
 CallDuringDinner();
 ContinueTalkingWhenYouSayNo();
 }
 }

我们现在有两个类,它们各自的方式都是令人厌烦的。 而且他们不需要从同一基类派生并分享共同的固有特点--他们只需要简单地满足契约而 IPest --协定,则很简单。 你只要 BeAnnoying 。 在这方面,我们可以对以下内容进行建模:


 class DiningRoom {

 DiningRoom(Person[] diningPeople, IPest[] pests) {.. . }

 void ServeDinner() {
 when diningPeople are eating,

 foreach pest in pests
 pest.BeAnnoying();
 }
 }

这里我们有一个餐厅,接受一些食客和一些害虫,--注意到这个界面的使用。 这意味着在我们的小世界中,害虫的成员实际上可以是Telemarketer对象或者家蝇对象。

在餐厅各自代表了在 eat, ServeDinner 提供服务时将调用,晚饭和我们 people. 在我们的小游戏,也就是当我们的害虫--每个害虫被指示为进行他们的工作是非常烦人的事通过IPest界面的方式。 通过这种方式,我们可以轻松地同时拥有中other,电话推销员和HouseFlys是只在每个属于自己的方式--我们非常不关心,我们已经从DiningRoom看见的对象,它是一种害虫,我们确实不关心它是什么,他们很可能会什么都在共同之处,

在的我们可能会使用一个interface,这个非常刻意pseudo-code示例( 拖得比我预想的长很多) 时只是要说明方面,在为我制定了一种东西,终于打开了灯 我为示例的愚蠢而道歉,但希望它能帮助你理解。 而且,要确定,你在这里收到的其他问题实际上涵盖了当今设计模式和开发方法中的界面使用范围。

我给学生的具体例子是他们应该写


List myList = new ArrayList();//programming to the List interface

代替


ArrayList myList = new ArrayList();//this is bad

这些看起来在一个短程序中完全相同,但是如果你在程序中使用 myList 100次,你可以开始看到一个不同。 第一个声明确保你只调用 myList 上由 List 接口( 所以没有 ArrayList 特定的方法) 定义的方法。 如果你通过这种方式对接口进行了编程,以后你就可以决定确实需要


List myList = new TreeList();

你只需要在那个地方修改你的代码。 那么你就会知道你的代码不做任何事情,从而将被打破的其余部分通过更改 实现,因为你曾对 接口。

你应该查看控制反转:

在这样的场景中,你不会编写:


IInterface classRef = new ObjectWhatever();

你将编写如下内容:


IInterface classRef = container.Resolve<IInterface>();

于你,这可能成为相关这将进入一个rule-based安装程序在 container 对象,并构造实际的object. 重要的是,你可以用其他类型的对象替换这个规则,你的代码仍然可以工作。

如果我们把台离开控制反转,可以编写代码,就知道它还可以跟特定 某个对象,它完成某些任务,但不是哪种类型的对象或者它是怎么运转的。

这将在传递参数时派上用场。

对于带括号的问题,也是如此,你如何编写一个方法来实现一个实现接口的对象? 是否可能"在 C# 中,你只需为参数类型使用接口类型,就像这样:?


public void DoSomethingToAnObject(IInterface whatever) {.. . }

这个插头插进"与执行特定操作的对象交谈"。 上面定义的方法知道从对象期望什么,它实现了IInterface中的一切,但它不关心它是什么类型的对象,只关心它是什么类型的对象,这就是接口真正是什么。

比如,你可能很熟悉计算器,在你的日子里很可能使用过一些,但是大多数时候它们都是不同的。 另一方面,你知道标准计算器应该如何工作,所以你可以使用它们全部,即使你不能使用每个计算器都没有的特定特性。

这是界面的美妙之处。 你可以编写一段代码,它知道它将得到传递给它的对象,它可以从。 它不关心它是什么类型的对象,只是它支持所需的行为。

让我给你一个具体的例子。

于 Windows 相关forms,指对我们有一个custom-built翻译而言 这里系统在窗体上循环显示控件,并将每个。 系统知道如何处理基本控件,比如the-type-of-control-that-has-a-Text-property和类似的基本内容,但对于任何基本的东西,它都是短暂的。

现在,由于控件继承自我们无法控制的pre-defined类,我们可以执行以下三种操作之一:

  1. 在我们的翻译系统中构建支持来检测它所使用的控制类型,并翻译正确的位( 维护恶梦)
  2. 生成基类的支持( 不可能,因为所有控件都继承自不同的pre-defined类)
  3. 添加接口支持

所以我们做了。 所有的控件都实现了 ILocalizable,它是一个接口,它提供了一个方法,可以通过翻译文本/规则的容器来翻译"自身本身"。 因此,表单不需要知道它所找到的控件类型,只需要实现特定的接口,并知道有一个方法可以调用它来本地化控件。

除了删除类之间不必要的耦合之外,使用接口是使代码易于测试的关键因素。 通过创建一个接口,它定义了你的类上的操作。可以使希望使用该功能的类直接使用它可以不依赖于你的实现类的能力。 如果稍后决定更改并使用其他实现,则只需要更改实现实现的代码部分。 代码的其余部分不需要更改,因为它依赖于接口,而不是实现类。

这在创建单元测试时非常有用。 在待测试的类你有,它取决于接口并将该接口的实例注入到类的构造函数或者属性 settor ( 或者允许它根据需要构建接口实例的工厂) 通过。 类在它的方法中使用提供的( 或者创建) 接口。 在你当你去写你的测试,可以模仿或者伪造,并提供一个接口,该接口的接口数据configured.进行响应 因为测试的类只处理接口,而不是你的具体实现,所以你可以这样做。 实现接口的任何类,包括你的模拟或者假类,都将执行。

下面编辑: 就是获取该文章的链接,Erich Gamma等方面论述它的在报价,"按接口编程,而不是一个实现。"

http://www.artima.com/lejava/articles/designprinciples.html

对接口的编程是说"我需要这个功能,而我不在乎它来自哪里。""

考虑( 在Java中),列表接口与ArrayList和LinkedList具体类。 如果我关心的是我拥有一个包含多个数据项的数据结构,我应该通过迭代访问,我将选择清单( 那是时间的99% ) 。 如果我知道需要从列表的任意一端插入constant-time插入/删除,我可以选择LinkedList具体实现( 或者更可能使用队列接口) 。 如果我知道我需要索引的随机访问,我会选择ArrayList的具体类。

对接口编程与我们在Java或者. NET 中看到的抽象接口毫无关系。 它甚至不是面向对象的概念。

它真正意味着的是不要混淆对象或者数据结构的内部。 使用抽象程序接口或者API与数据交互。 在Java或者 C# 中,意味着使用公共属性和方法而不是原始字段访问。 对于C,这意味着使用函数而不是原始指针。

编辑:与数据库一起使用,它意味着使用视图和存储过程而不是直接表访问。

如果你用Java编程,JDBC就是一个很好的例子。 JDBC定义了一组接口,但并没有说明实现。 你的应用程序可以根据这组接口编写。 从理论上讲,你选择一些JDBC驱动程序,你的应用程序就会工作。 如果你发现我们有一个快一点或者"更佳"或者更便宜的JDBC驱动程序或者出于其他原因,你可以再次在理论re-configure你的属性文件,并在应用程序中,而不必进行任何更改,你的应用程序仍然起作用。

听起来你理解接口是如何工作的,但不确定什么时候使用它们以及它们提供的优势。 下面是一个接口有意义的例子:


//if I want to add search capabilities to my application and support multiple search
//engines such as google, yahoo, live, etc.

interface ISearchProvider
{
 string Search(string keywords);
}

然后我可以创建 GoogleSearchProvider,YahooSearchProvider,LiveSearchProvider等。


//if I want to support multiple downloads using different protocols
//HTTP, HTTPS, FTP, FTPS, etc.
interface IUrlDownload
{
 void Download(string url)
}

//how about an image loader for different kinds of images JPG, GIF, PNG, etc.
interface IImageLoader
{
 Bitmap LoadImage(string filename)
}

然后创建 JpegImageLoader,GifImageLoader,PngImageLoader等。

大多数 Add-Ins 和插件系统都能处理接口。

另一个流行的用途是存储库模式。 假设我想从不同的源加载邮政编码列表


interface IZipCodeRepository
{
 IList<ZipCode> GetZipCodes(string state);
}

后来我就可以创建一个 XMLZipCodeRepository,SQLZipCodeRepository 。CSVZipCodeRepository 。等等 为我的网络应用程序之前,我通常会创建XML存储库在早期,这样我就可以获得并运行一些内容到Sql数据库来准备就绪。 一旦数据库准备就绪,我就编写一个SQLRepository来替换XML版本。 剩下的代码保持不变,因为它从接口运行 soley 。

方法可以接受以下接口:


PrintZipCodes(IZipCodeRepository zipCodeRepository, string state)
{
 foreach (ZipCode zipCode in zipCodeRepository.GetZipCodes(state))
 {
 Console.WriteLine(zipCode.ToString());
 }
}

编程到接口是很棒的,它促进了松散耦合。 就像 @lassevk 所提到的,控制反转是非常有用的。

相反,望向实体主体是一个视频序列

它通过一个硬编码的( 强耦合示例),然后查看接口,最后进入一个 ioc/di工具( NInject )

除了已经选择的答案( 这里有各种各样的信息) 之外,我强烈建议你先获取 Head头设计模式 。 这是一个很容易阅读,并将直接回答你的问题,解释为什么它是重要的,并向你展示很多的编程模式,你可以用它来利用这一原则( 等等) 。

...