Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Language agnostic “编程到接口”是什么意思?_Language Agnostic_Oop_Interface - Fatal编程技术网

Language agnostic “编程到接口”是什么意思?

Language agnostic “编程到接口”是什么意思?,language-agnostic,oop,interface,Language Agnostic,Oop,Interface,我已经见过几次提到这一点,我不清楚这是什么意思。你什么时候为什么要这样做 我知道接口是做什么的,但我不清楚这一点,这让我觉得我错过了正确使用它们的机会 如果你要做的是: IInterface classRef = new ObjectWhatever() 您可以使用任何实现IInterface的类?你什么时候需要这样做?我能想到的唯一一件事是,如果你有一个方法,你不确定会传递什么对象,除了它实现IInterface。我想不出你需要多久做一次 另外,如何编写一个方法来接收实现接口的对象?这可能吗

我已经见过几次提到这一点,我不清楚这是什么意思。你什么时候为什么要这样做

我知道接口是做什么的,但我不清楚这一点,这让我觉得我错过了正确使用它们的机会

如果你要做的是:

IInterface classRef = new ObjectWhatever()
您可以使用任何实现IInterface的类?你什么时候需要这样做?我能想到的唯一一件事是,如果你有一个方法,你不确定会传递什么对象,除了它实现IInterface。我想不出你需要多久做一次


另外,如何编写一个方法来接收实现接口的对象?这可能吗?

您应该研究控制反转:

在这种情况下,您不会这样写:

IInterface classRef = new ObjectWhatever();
你可以这样写:

IInterface classRef = container.Resolve<IInterface>();
public void DoSomethingToAnObject(IInterface whatever) { ... }
这将直接插入到与执行特定操作的对象的对话中。上面定义的方法知道从对象可以得到什么,它实现了IInterface中的所有内容,但它不关心它是哪种类型的对象,只关心它遵守契约,这就是接口

例如,你可能对计算器很熟悉,在你的生活中可能用过不少,但大多数时候它们都是不同的。另一方面,你知道一个标准计算器应该如何工作,所以你可以使用它们,即使你不能使用每个计算器都没有的特定功能

这就是界面之美。您可以编写一段代码,它知道它将获得传递给它的对象,它可以期望从中获得某些行为。它一点也不关心它是什么类型的对象,只关心它支持所需的行为

让我给你举个具体的例子

public interface IPricable
{
    int Price { get; }
}

public interface ICar : IPricable

public abstract class Article
{
    public int Price { get { return ... } }
}

public class Car : Article, ICar
{
    // Price does not need to be defined here
}
我们有一个为windows窗体定制的翻译系统。该系统通过表单上的控件进行循环,并在每个控件中翻译文本。系统知道如何处理基本控件,如-type-of-control-that-has-Text-property和类似的基本内容,但对于任何基本的东西,它都不够

现在,由于控件继承自我们无法控制的预定义类,我们可以做以下三件事之一:

构建对翻译系统的支持,以专门检测其使用的控件类型,并翻译正确的位 不可能将支持构建到基类中,因为所有控件都继承自不同的预定义类 添加接口支持
所以我们做了3号。我们所有的控件都实现了ILocalizable,这是一个接口,它为我们提供了一种方法,能够将自身翻译成翻译文本/规则的容器。因此,表单不需要知道它找到了哪种控件,只需要知道它实现了特定的接口,并且知道有一种方法可以调用来本地化控件。

对接口编程就是说,我需要这个功能,我不在乎它来自何方


考虑在Java中,列表接口与ArrayList和LinkedList具体类的区别。如果我所关心的是我有一个包含多个数据项的数据结构,我应该通过迭代访问这些数据项,那么我会选择一个列表,这是99%的时间。如果我知道我需要从列表的任意一端进行固定时间的插入/删除,我可能会选择LinkedList的具体实现,或者更可能的是,使用接口。如果我知道我需要通过索引进行随机访问,我会选择ArrayList具体类。

当您有一组类似的类时,它会使您的代码更易于扩展和维护。我是一个初级程序员,所以我不是专家,但我刚刚完成了一个需要类似东西的项目

我使用客户端软件与运行医疗设备的服务器对话。我们正在开发此设备的新版本,其中包含一些客户有时必须配置的新组件。有两种类型的新组件,它们不同,但也非常相似。基本上,我必须创建两个配置表单,两个列表类,所有的两个

我决定最好为每个控件类型创建一个抽象基类,该类将包含几乎所有的实际逻辑,然后派生类型来处理这两个组件之间的差异。然而,如果我不得不一直担心类型的话,基类就不能在这些组件上执行操作了,它们本来可以,但是每个方法中都会有一个if语句或开关


我为这些组件定义了一个简单的接口,所有的基类都与这个接口通信。现在,当我改变一些东西时,它几乎在任何地方都能“正常工作”,我没有代码重复。

为了补充现有的文章,当开发人员同时在不同的组件上工作时,有时在大型项目中编写接口代码会有所帮助。你所需要做的就是把它弄脏
在其他开发人员为您正在实现的接口编写代码时,预先定义接口并向其编写代码。

除了消除类之间不必要的耦合之外,使用接口是使代码易于测试的关键因素。通过创建定义类上的操作的接口,可以允许希望使用该功能的类在不直接依赖于实现类的情况下使用该功能。如果以后决定更改并使用不同的实现,则只需更改实例化实现的代码部分。代码的其余部分不需要更改,因为它取决于接口,而不是实现类

这在创建单元测试时非常有用。在被测试的类中,您让它依赖于接口,并将接口的实例注入到类或工厂中,该类或工厂允许它根据需要通过构造函数或属性设置器构建接口的实例。该类在其方法中使用提供的或创建的接口。在编写测试时,可以模拟或伪造接口,并提供一个接口,该接口使用单元测试中配置的数据进行响应。您可以这样做,因为您的被测类只处理接口,而不处理您的具体实现。任何实现接口的类,包括模拟类或伪类,都可以

编辑:下面是一篇文章的链接,Erich Gamma在文章中讨论了他的引用,程序到接口,而不是实现


如果您使用Java编程,JDBC就是一个很好的例子。JDBC定义了一组接口,但没有说明实现。可以根据这组接口编写应用程序。理论上,选择一些JDBC驱动程序,应用程序就可以正常工作了。如果您发现有一个更快、更好或更便宜的JDBC驱动程序,或者出于任何原因,您可以再次在理论上重新配置属性文件,而不必对应用程序进行任何更改,您的应用程序仍然可以工作。

我给学生的具体示例是,他们应该编写

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

ArrayList myList = new ArrayList(); // this is bad
在一个简短的程序中,它们看起来完全相同,但是如果您在程序中继续使用myList 100次,您就会开始看到不同之处。第一个声明确保只调用列表接口定义的myList方法,因此没有特定于ArrayList的方法。如果您已经以这种方式对界面进行了编程,那么稍后您可以决定您真正需要什么

List myList = new TreeList();
你只需要在这一点上修改你的代码。您已经知道,代码的其余部分不会做任何会因更改实现而被破坏的事情,因为您已编程到接口

class Person implements AbstractEmployee, AbstractFriend {
}
我认为,在讨论方法参数和返回值时,好处更为明显。以此为例:

public ArrayList doSomething(HashMap map);
该方法声明将您与两个具体的实现ArrayList和HashMap联系起来。一旦从其他代码调用该方法,对这些类型的任何更改都可能意味着您也必须更改调用代码。最好对接口进行编程

public List doSomething(Map map);

现在,返回什么类型的列表,或者作为参数传入什么类型的映射都无关紧要了。您在doSomething方法中所做的更改不会强制您更改调用代码。

想象您有一个名为“Zebra”的产品,可以通过插件进行扩展。它通过在某个目录中搜索DLL来查找插件。它加载所有这些DLL并使用反射查找实现IZebraPlugin的任何类,然后调用该接口的方法与插件通信

这使得它完全独立于任何特定的插件类-它不关心类是什么。它只关心它们是否满足接口规范

接口是这样定义扩展点的一种方式。与接口对话的代码是松散耦合的——事实上,它根本不与任何其他特定代码耦合。它可以与多年后由从未见过原始开发人员的人编写的插件进行交互操作


您可以改为使用带有虚拟函数的基类-所有插件都将从基类派生。但这是一个更大的限制,因为一个类只能有一个基类,而它可以实现任意数量的接口。

对接口编程非常棒,它促进了松散耦合。正如@lassevk所提到的,控制反转是这种方法的一个重要用途

public List doSomething(Map map);
此外,还要研究坚实的原则


它经历了一个硬编码的强耦合示例,然后研究了接口,最后发展到一个IoC/DI工具NInject

这里有一些关于这个问题的精彩答案,这些问题涉及到接口和松耦合代码、控制反转等各种各样的细节。有一些相当令人兴奋的讨论,所以我想提出异议 为了理解为什么接口是有用的,我们需要把事情分解一下

当我第一次接触接口时,我也对它们的相关性感到困惑。我不明白你为什么需要它们。如果我们使用像Java或C这样的语言,我们已经有了继承,我认为接口是一种较弱的继承形式,我想,为什么要麻烦呢?从某种意义上说,我是对的,你可以把接口看作是一种弱继承形式,但除此之外,我最终理解了它们作为一种语言结构的使用,因为我把它们看作是一种对共同特征或行为进行分类的方法,而这些特征或行为可能由许多不相关的对象类表现出来

例如,假设您有一个SIM卡游戏,并拥有以下类别:

家蝇类遗传昆虫{ void flyroundYourHead{} 虚空着陆{} } 类电话销售员继承人{ 晚餐期间无效呼叫{} void continuetalking whenyousayno{} } 显然,这两个对象在直接继承方面没有任何共同之处。但是,你可以说他们都很讨厌

让我们假设我们的游戏需要有一些随机的东西,当游戏玩家吃晚餐时,这些东西会激怒他们。这可能是家蝇,也可能是电话销售员,或者两者兼而有之——但你如何通过一个单一的功能将两者都考虑进去呢?你如何要求每种不同类型的物体以同样的方式做他们讨厌的事情

认识到这一点的关键是,电话销售员和家蝇都有一个共同的松散解释的行为,即使他们在建模方面一点都不相似。因此,让我们制作一个双方都可以实现的接口:

接口IPest{ 虚空无影; } 家蝇类继承昆虫类{ void flyroundYourHead{} 虚空着陆{} 虚无无{ 绕着你的头飞; 登陆; } } 类Telemarketer继承实现IPest的人{ 晚餐期间无效呼叫{} void continuetalking whenyousayno{} 虚无无{ 晚餐时打电话; 当你不在时,继续行走; } } 我们现在有两个类,每个类都有自己的烦人之处。它们不需要从相同的基类派生,也不需要共享共同的固有特性——它们只需要满足IPest的契约——契约很简单。你只需要做点傻事就行了。在这方面,我们可以建立以下模型:

班级餐厅{ 餐厅里的人,我最讨厌的害虫{…} 无效服务器{ 当人们吃饭的时候, 害虫中的每种害虫 贝恩诺因害虫; } } 这里我们有一个餐厅,可以容纳许多食客和害虫——注意界面的使用。这意味着在我们的小世界中,有害生物阵列的一个成员实际上可能是电话销售员对象或家蝇对象

当提供晚餐并且餐厅里的人应该吃饭时,就叫Servediner方法。在我们的小游戏中,当我们的害虫完成它们的工作时,每个害虫都会通过IPest接口被指示为令人讨厌。通过这种方式,我们可以很容易地让电话销售员和家蝇以各自的方式感到恼火——我们只关心我们的餐厅物品中有有害的东西,我们并不真正关心它是什么,他们之间可能没有任何共同点


这个精心设计的伪代码示例拖得比我预期的时间长得多,只是为了说明在何时可以使用接口这一点上,它最终为我打开了一盏灯。我为这个例子的愚蠢提前道歉,但希望它有助于你的理解。而且,可以肯定的是,您在这里收到的其他帖子回答确实涵盖了当今设计模式和开发方法中接口的使用范围

在Java中,这些具体类都实现了CharSequence接口:

CharBuffer,String,StringBuffer,StringBuilder

这些具体的类除了Object之外没有一个公共的父类,所以它们之间没有任何关联,除了它们各自与字符数组、表示或操作字符数组有关。例如,字符串对象实例化后不能更改字符串的字符,而StringBuffer或StringBuilder的字符可以编辑

然而,这些类中的每一个都能够适当地实现CharSequence接口方法:

char charAt(int index)
int length()
CharSequence subSequence(int start, int end)
String toString()
在某些情况下,用于接受字符串的Java类库类已被修改为现在接受CharSequence接口。因此,如果您有一个StringBuilder实例,它可以在实现CharSequence接口时传递StringBuilder本身,而不是提取一个String对象(这意味着实例化一个新的对象实例)

class Person implements AbstractEmployee, AbstractFriend {
}
某些类实现的可追加接口在任何情况下都有相同的好处,其中 可以将字符附加到基础具体类对象实例的实例中。所有这些具体类都实现了可追加接口:

BufferedWriter、CharrayWriter、CharBuffer、FileWriter、FilterWriter、LogStream、OutputStreamWriter、PipedWriter、PrintStream、PrintWriter、StringBuffer、StringBuilder、StringWriter、Writer


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

// 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等

大多数插件和插件系统都使用接口

public List doSomething(Map map);
另一个流行的用法是存储库模式。假设我想加载来自不同来源的邮政编码列表

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

所以,为了做到这一点,接口的优点是我可以将方法的调用与任何特定的类分开。而是创建一个接口实例,其中实现是从我选择的实现该接口的类中给出的。因此,允许我有许多类,它们具有相似但略有不同的功能,在某些情况下,与接口意图相关的情况并不关心它是哪个对象


例如,我可以有一个移动界面。一种方法可以使某些东西“移动”,任何实现移动接口的对象(人、车、猫)都可以传入并被告知移动。如果没有这个方法,每个人都知道它是什么类型的类

这对单元测试也有好处,您可以将自己满足接口要求的类注入依赖于接口的类中

除了已经选择的答案和这里的各种信息性帖子之外,我强烈建议您获取一份。这本书非常容易阅读,可以直接回答您的问题,解释为什么它很重要,并向您展示许多可用于利用该原理和其他原理的编程模式。

如果我正在编写一个新的类游泳者来添加功能swim,并且需要使用类对象say Dog,这个Dog类实现了接口Animal,该接口声明swim

在动物等级的顶端,它是非常抽象的,而在底层,它是非常具体的。我对接口编程的想法是,当我编写游泳者类时,我想针对尽可能高的层次结构(在本例中是动物对象)编写代码。接口没有实现细节,因此使代码松散耦合


实现细节可以随着时间的推移而改变,但是,它不会影响其余的代码,因为您所交互的是接口,而不是实现。你不在乎实现是什么样的。。。您所知道的是,将有一个类实现该接口。

对接口进行编程与Java或.NET中的抽象接口完全无关。这甚至不是一个面向对象的概念

class Person implements AbstractEmployee, AbstractFriend {
}
这意味着不要乱搞对象或数据结构的内部。使用抽象程序接口或API与数据交互。在Java或C中,这意味着使用公共属性和方法而不是原始字段访问。对于C,这意味着使用函数而不是原始指针

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

C++解释

将接口视为类的公共方法

然后,您可以创建一个“依赖于”这些公共方法的模板,以便执行它自己的函数—它在类公共接口中定义函数调用。假设这个模板是一个容器,就像一个向量类,它所依赖的接口是一个搜索算法

定义函数/接口向量调用的任何算法类都将满足原始答复中解释的“契约”。算法甚至不需要是相同的基类;唯一的要求是在算法中定义向量依赖于接口的函数/方法

所有这些的要点是,您可以提供任何不同的搜索算法/类,只要它提供向量依赖于气泡搜索、顺序搜索、快速搜索的接口

您可能还希望设计其他容器列表和队列,通过让它们实现您的搜索算法所依赖的接口/契约来利用与Vector相同的搜索算法

这节省了OOP原则“代码重用”的时间,因为您可以一次编写算法,而不是针对您创建的每个新对象一次又一次地编写算法,而不会因过度增长的继承树而使问题过于复杂

至于"遗漏"事情如何运作,;毕 至少在C++中是GTIME,因为这是标准模板库的大部分框架的操作方式。 当然,当使用继承和抽象类时,接口编程的方法会发生变化;但原理是一样的,您的公共函数/方法是您的类接口

class Person implements AbstractEmployee, AbstractFriend {
}

这是一个巨大的主题,也是设计模式的基石原则之一。

有很多解释,但要让它更简单。以列表为例。可以通过以下方式实现列表:

内部数组 链表 其他实现 通过构建一个接口,比如说一个列表。您只需要编码列表的定义或列表在现实中的含义

您可以在内部使用任何类型的实现,例如数组实现。但是,假设您希望出于某种原因(比如bug或性能)更改实现。然后您只需将声明列表ls=newarraylist更改为List ls=newlinkedlist

在代码中没有其他地方,您将不得不更改任何其他内容;因为其他一切都建立在列表的定义之上

Q:-。。。您可以使用任何实现接口的类吗? A:是的

Q:-。。。你什么时候需要这样做? 答:-每次需要实现接口的类时

public List doSomething(Map map);
注意:我们无法实例化不是由类实现的接口-True

为什么? 因为接口只有方法原型,没有定义,只有函数名,没有它们的逻辑 AnIntf anInst=新的Aclass; //只有Aclass实现了一个INTF,我们才能这样做。 //一个列表将有一个类引用

注意:现在我们可以理解如果Bclass和Cclass实现相同的Dintf会发生什么

我们拥有:相同的接口原型,接口中的函数名,调用不同的实现

参考书目:
短篇故事:邮递员被要求回家后,收到信封,包括信件、文件、支票、礼品卡、申请表、写明地址的情书,以便投递

假设没有封面,让邮递员一个接一个地回家,接收所有的东西,然后把它们交给其他人,邮递员可能会感到困惑

因此,最好在我们的故事中用封面来包装它,这是一个界面,然后他会做好他的工作

现在,邮递员的工作是接收和递送封面,只是他不会在意封面里面的内容

创建接口类型不是实际类型,而是使用实际类型实现它

创建接口意味着您的组件可以轻松地融入代码的其余部分

我给你举个例子

您的飞机界面如下所示

interface Airplane{
    parkPlane();
    servicePlane();
}
假设在平面的控制器类中有如下方法

parkPlane(Airplane plane)

在您的程序中实现。它不会破坏你的代码。 我的意思是,它不需要改变,只要它接受作为飞机的论点

因为它可以接受任何类型的飞机,不管是真正的飞机、飞翼飞机、高飞翼飞机、战斗机等等

此外,在集合中:

列表平面;//我将乘坐你所有的飞机

下面的示例将明确您的理解

你有一架战斗机来执行它,所以

public class Fighter implements Airplane {

    public void  parkPlane(){
        // Specific implementations for fighter plane to park
    }
    public void  servicePlane(){
        // Specific implementatoins for fighter plane to service.
    }
}
HighFlyer和其他类别也一样:

public class HighFlyer implements Airplane {

    public void  parkPlane(){
        // Specific implementations for HighFlyer plane to park
    }

    public void  servicePlane(){
        // specific implementatoins for HighFlyer plane to service.
    }
}
现在想想你的控制器类多次使用飞机

假设您的控制器类是ControlPlane,如下所示

public Class ControlPlane{ 
 AirPlane plane;
 // so much method with AirPlane reference are used here...
}
这里的魔术来了,因为你可以使你的新飞机类型的实例尽可能多,你不改变ControlPlane类的代码

您可以添加一个实例

JumboJetPlane // implementing AirPlane interface.
AirBus        // implementing AirPlane interface.

您也可以删除以前创建的类型的实例。

此外,我在这里看到了许多很好的解释性答案,因此我想在这里给出我的观点,包括使用此方法时注意到的一些额外信息

单元测试

在过去的两年里,我写了一个爱好项目,我没有为它写单元测试。在写了大约50K行之后,我发现编写单元测试是非常必要的。 我没有使用接口或非常节省。。。当我进行第一次单元测试时,我发现它很复杂。为什么?

因为我必须创建很多类实例,作为类变量和/或参数用于输入。因此,这些测试看起来更像是集成测试,因为所有的类都是绑定在一起的,所以必须创建一个完整的类“框架”

对接口的恐惧 所以我决定使用接口。我担心的是,我必须在所有使用过的类中多次实现所有功能。在某种程度上这是正确的,但是,通过使用继承,它可以减少很多

接口与继承的结合 我发现这个组合很好用。我举一个非常简单的例子

public interface IPricable
{
    int Price { get; }
}

public interface ICar : IPricable

public abstract class Article
{
    public int Price { get { return ... } }
}

public class Car : Article, ICar
{
    // Price does not need to be defined here
}

这种方式不需要复制代码,但仍有使用汽车作为接口ICar的好处。

让我们先从一些定义开始:

接口。由对象操作定义的所有签名集称为 对象的接口

类型n。特定接口

上面定义的接口的一个简单示例是所有PDO对象方法,如查询、提交、关闭等,作为一个整体,而不是单独的。这些方法(即其接口)定义了可发送到对象的完整消息集和请求

上面定义的类型是一个特定的接口。我将使用制作好的形状界面来演示:draw、getArea、GetPermission等

如果对象是数据库类型,则表示它接受数据库接口、查询、提交等的消息/请求。。对象可以有多种类型。只要数据库对象实现其接口,它就可以是shape类型,在这种情况下,这将是子类型

许多对象可以具有许多不同的接口/类型,并以不同的方式实现该接口。这允许我们替换对象,让我们选择使用哪个对象。也称为多态性

客户端只知道接口,而不知道实现


所以在本质上,对一个接口进行编程需要创建一些抽象类,比如只指定接口的形状,比如draw、getCoordinates、getArea等等。。然后让不同的具体类实现这些接口,如圆形类、方形类、三角形类。因此,编程到接口而不是实现。

我是这个问题的后来者,但我想在这里提到的是,到接口而不是实现的行程序在GoF Gang of Four Design Patterns一书中进行了一些很好的讨论

它说,在第页。18:

程序到接口,而不是实现

不要将变量声明为特定具体类的实例。相反,只提交到由抽象类定义的接口。您会发现这是本书中设计模式的一个共同主题

除此之外,它还始于:

仅根据抽象类定义的接口操作对象有两个好处:

只要对象符合客户机期望的接口,客户机就不知道他们使用的对象的特定类型。 客户端仍然不知道实现这些对象的类。客户端只知道定义接口的抽象类。 所以换句话说,不要把它写成你的类,这样它就有了鸭子的嘎嘎方法,狗的树皮方法,因为它们对于类或子类的特定实现来说太具体了。相反,请使用足够通用的名称编写方法,以便在基类中使用,例如giveSound或move,以便它们可以用于鸭子、狗甚至汽车,然后,类的客户端可以说。giveSound,而不是在发出要发送到对象的正确消息之前考虑是否使用嘎嘎或吠叫,甚至确定类型。

接口的代码不是实现与Java无关,也不是它的接口构造

这一概念在《模式/四人帮》一书中非常突出,但很可能早在这之前就存在了。这个概念肯定早在Java出现之前就存在了

Java接口构造是为了帮助实现这一想法而创建的,人们已经过于关注作为意义中心的构造,而不是原始意图。但是,在爪哇、C++、C等中,我们有公共和私有的方法和属性。 这意味着只需与对象或系统的公共接口进行交互。不要担心,甚至不要预测它如何做它内部做的事情。不要担心它是如何实现的。在面向对象代码中,这就是为什么我们有公共和私有方法/属性。我们打算使用公共方法,因为私有方法只在类内部使用。它们构成类的实现,可以根据需要进行更改,而无需更改公共接口。假设在功能方面,每次您使用相同的参数调用类上的方法时,该类上的方法将执行相同的操作,并获得相同的预期结果。它允许作者更改类的工作方式及其实现,而不破坏人们与它的交互方式

您可以编程到接口,而不是不使用接口构造的实现。你可以将程序编程到C++中,而不是接口实现。您可以更稳健地集成两个大型企业系统,只要它们通过公共接口契约进行交互,而不是对系统内部的对象调用方法。对于相同的输入参数,接口应始终以相同的预期方式作出反应;如果实现到接口而不是实现。这个概念在很多地方都适用

动摇认为
Java接口与“编程到接口,而不是实现”的概念有着千丝万缕的联系。它们可以帮助应用概念,但它们不是概念

接口就像契约,您希望实现类实现契约接口中编写的方法。由于Java不提供多重继承,对接口进行编程是实现多重继承的好方法

如果您有一个类a已经在扩展另一个类B,但您希望该类a也遵循某些准则或实现某个契约,那么您可以通过编程到接口策略来实现。编程到接口允许无缝更改接口定义的契约的实现。它允许契约和特定实现之间的松散耦合

IInterface classRef=新对象

您可以使用任何实现IInterface的类?你什么时候需要这样做

看看这个SE问题,作为一个很好的例子

使用接口会影响性能吗

如果是,多少钱

对。它将在亚秒内产生轻微的性能开销。但是,如果您的应用程序需要动态更改接口的实现,则不要担心性能影响

您如何避免它而不必维护两位代码

如果您的应用程序需要多个接口实现,请不要尝试避免它们。如果接口与一个特定的实现没有紧密耦合,您可能必须部署补丁来将一个实现更改为另一个实现

一个很好的用例:战略模式的实施:


编程到接口是有利的,即使我们不依赖抽象

对接口编程迫使我们使用对象的上下文适当子集。这很有帮助,因为它:

防止我们做与环境不符的事情,以及 让我们在将来安全地更改实现。

例如,考虑一个实现朋友和雇员接口的人类。

class Person implements AbstractEmployee, AbstractFriend {
}
在这个人的生日背景下,我们编程到Friend界面,以防止把这个人当作员工对待

在员工的工作环境中,我们对员工界面进行编程,以防止模糊工作场所的边界

function workplace() {
    const employee: Employee = new Person("Kathryn");
    employee.DoWork();
}
太好了。我们在不同的环境中表现良好,我们的软件运行良好

在遥远的将来,如果我们的业务改变为与狗一起工作,我们可以相当容易地更改软件。首先,我们创建一个Dog类,它实现了Friend和Employee。然后,我们安全地把新的人换成新的狗。即使两个函数都有数千行代码,这种简单的编辑也会起作用,因为我们知道以下是正确的:

功能方仅使用个人的朋友子集。 功能workplace仅使用人员的员工子集。 类Dog实现了Friend和Employee接口。 另一方面,如果任何一方或工作场所针对个人进行编程,则存在同时使用特定于个人的代码的风险。从一个人到另一只狗的转换需要我们梳理代码,以删除狗不支持的任何特定于人的代码


寓意:对接口进行编程有助于我们的代码行为得体,并为更改做好准备。它还使我们的代码依赖于抽象,这带来了更多的优势。

程序到接口意味着不提供正确的硬代码,这意味着您的代码应该在不破坏先前功能的情况下进行扩展。只是扩展,而不是编辑前面的代码。

接口程序是GOF手册中的一个术语。我不会直接说它与java接口有关,而是与真正的接口有关。要实现干净的层分离,您需要在系统之间创建一些分离,例如:假设您有一个想要使用的具体数据库,您永远不会对数据库编程,而是对存储接口编程。同样,您永远不会编程到Web服务,而是编程到客户机接口。这样你就可以很容易地交换东西了

我发现这些规则对我有帮助:

一,。当一个对象有多种类型时,我们使用java接口。如果我只有一个物体,我看不出有什么意义。如果某个想法至少有两个具体实现,那么我将使用java接口

二,。如前所述,如果您希望将外部系统存储系统与您自己的系统本地数据库分离,那么还需要使用接口


请注意,在使用它们时,有两种方法可以考虑。希望这有帮助

为什么在一开始就提到IoC,因为这只会增加更多的混乱。我同意,我想说的是,针对接口编程只是一种使IoC更容易和可靠的技术。如果你能记住并且

r程序需要优化,在编译之前,您可能希望将接口声明替换为实际实现。因为使用接口会增加间接级别,从而影响性能。把你的程序代码分发到接口上…@Ande Turner:这是个糟糕的建议。1.您的程序需要优化并不是交换接口的好理由!然后你说把你编程的代码分发到接口上。。。那么您建议给定需求1,然后发布次优代码?!?这里的大多数答案都不太正确。它根本不意味着甚至不意味着使用interface关键字。接口是关于如何使用与合同同义的内容的规范。与此不同的是执行,这是如何履行合同的。仅针对方法/类型的保证进行编程,这样,当方法/类型以符合约定的方式更改时,它不会使用它破坏代码。@这实际上是整个页面上的最佳答案。至少对于标题中的问题来说,因为这里至少有3个完全不同的问题…像这样的问题的基本问题是,它假设编程到一个接口意味着将所有东西包装在一个抽象接口中,如果你认为这个术语早于java风格的抽象接口的概念,那就很傻了。它不仅在一个更好的驱动程序可用的情况下有用,它还可以彻底改变数据库供应商。JDBC非常糟糕,所以需要替换。另找一个例子。JDBC是不好的,但不是因为接口与实现或抽象级别的关系。因此,为了说明这个概念,它是完美的。另一个要考虑的是,在某些情况下,对于可能恼人的事物有一个接口可能是有用的,并且有各种各样的对象实现作为一个NOP的麻烦。此接口可能存在于恼人事物的接口的位置,或者作为恼人事物的接口的补充。如果两个接口都存在,则恼人事物的接口应继承自可能恼人事物的接口。使用此类接口的缺点是,实现可能会因实现大量烦人的存根方法而负担过重。其优点是…这些方法不是用来表示抽象方法的-它们的实现与关注接口的问题无关。封装行为(如IPest)被称为策略模式,以防有人有兴趣跟进关于该主题的更多资料…有趣的是,您没有指出,因为IPest[]中的对象是IPest引用,所以您可以调用BeAnnoying,因为它们具有该方法,而您不能在没有强制转换的情况下调用其他方法。不过,每个对象都将调用单独的BeAnnoying方法。非常好的解释。。。我只需要在这里说一句:我从来没有听说过接口是一种松散的继承机制,但我知道继承被用作定义接口的一种糟糕的机制。例如,在常规Python中,你总是这样做。完全同意,即做什么与如何做之间的独立性。通过将一个系统沿着独立的组件进行划分,您最终得到了一个简单且可重用的系统。正如创建ClojureIt的人所看到的,像CharSequence这样的糟糕接口非常贫乏。我希望Java和.NET允许接口具有默认实现,这样人们就不会纯粹为了最小化样板代码而削减接口。给定任何合法的CharSequence实现,您可以仅使用上述四种方法模拟String的大多数函数,但许多实现可以通过其他方式更有效地执行这些函数。不幸的是,即使CharSequence的特定实现将所有内容都保存在一个char[]中,并且可以快速执行许多……操作,如indexOf,不熟悉CharSequence特定实现的调用方不可能要求它这样做,而不必使用charAt来检查每个字符。请再次阅读此采访:Gamma当然是在谈论接口的OO概念,而不是JAVA或C特殊类。问题是,大多数人都认为他在谈论关键词,所以我们现在有很多非必需的东西。非常好的采访。请注意未来的读者,采访共有四页。我几乎会在看到它之前关闭浏览器。最好的答案。Gamma在这里给出了类似的解释:见第2页。他指的是面向对象的概念,但你是对的:它比那个大。第一句话说明了一切。这应该是可以接受的答案。假设你没有太广泛的接口,也就是说。评论不用于扩展讨论;这段对话已经结束了。我哈哈
我想问一下您提到第一个声明的原因,它确保您只调用列表接口定义的myList上的方法,因此没有特定于ArrayList的方法。如果您已经以这种方式对接口进行了编程,那么稍后您可以决定您确实需要List myList=new TreeList;你只需要在这一点上修改你的代码。也许我误解了,我想知道如果您想确保只调用myList上的方法,为什么需要将ArrayList更改为TreeList?@user3014901您可能需要更改正在使用的列表类型的原因有很多。例如,可能有更好的查找性能。关键是,如果您编程到列表接口,那么以后将代码更改为其他实现就更容易了。