“编程接口”。 这是什么意思?

可能重复:
什么意思是“编程接口”?

我不断遇到这个词:

编程到一个接口。

究竟是什么意思? 现实生活中的devisescheme将受到高度赞赏。

简而言之,不要以某种方式写你的课程

我依靠这个特定的class级去做我的工作

你写的方式说,

我依靠任何能够做这件事的 class级来做我的工作。

第一个例子代表了一个依赖于具体实现来完成工作的类。 本质上,这不是很灵活。

第二个例子表示写入接口的类。 它并不关心你使用什么具体的对象,它只是在意它实现了某些行为。 这使得类更灵活,因为它可以提供任何数量的具体实现来完成它的工作。

作为一个例子,一个特定的类可能需要执行一些日志logging。 如果您编写的类依赖于TextFileLogger,则该类永远被迫将其日志logging写出到文本文件中。 如果要更改日志logging的行为,则必须更改该类本身。 这门课与logging器紧密结合。

但是,如果您编写的类依赖于一个ILogger接口,然后为该类提供一个TextFileLogger,那么您将完成同样的任务,但具有更多灵活性的额外好处。 您可以随意提供任何其他types的ILogger,而无需更改类本身。 这个类和它的logging器现在是松散耦合的,而且你的类更加灵活。

接口是相关方法的集合,只包含这些方法的签名 – 而不是实际的实现。
如果一个类实现了一个接口( class Car implements IDrivable ),它必须为接口中定义的所有签名提供代码。

基本示例:
你必须上课汽车和自行车。 两者都实现了接口IDrivable:

 interface IDrivable { void accelerate(); void brake(); } 

 class Car implements IDrivable { void accelerate() { System.out.println("Vroom"); } void brake() { System.out.println("Queeeeek");} } 

 class Bike implements IDrivable { void accelerate() { System.out.println("Rattle, Rattle, ..."); } void brake() { System.out.println("..."); } } 

现在让我们假设你有一个对象的集合,都是“可驱动的”(他们的类都实现了IDrivable):

 List<IDrivable> vehicleList = new ArrayList<IDrivable>(); list.add(new Car()); list.add(new Car()); list.add(new Bike()); list.add(new Car()); list.add(new Bike()); list.add(new Bike()); 

如果您现在想要遍历该集合,则可以依赖这样的事实:该集合中的每个对象都实现了accelerate()

 for(IDrivable vehicle: vehicleList) { vehicle.accelerate(); //this could be a bike or a car, or anything that implements IDrivable } 

通过调用这个接口方法,你不是编程到一个实现,而是编程到一个接口 – 一个确保调用目标实现特定function的协议。
使用inheritance可以实现相同的行为,但是从公共基类派生导致紧密耦合,这可以使用接口避免。

多态性取决于编程到接口,而不是实现。

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

  1. 只要客体遵守客户期望的界面,客户就不会意识到他们使用的特定types的客体。
  2. 客户仍然不知道实现这些对象的类。 客户只知道定义接口的抽象类。

这极大地减less了子系统之间的实现依赖关系,从而导致了编程原理到一个接口。

请参阅工厂方法模式以进一步推理此devise。

来源:GOF的“ devise模式:可重用面向对象软件的元素 ”

另请参阅: 工厂模式。 何时使用工厂方法?

真实的例子是很多的。 其中之一:

对于JDBC,您正在使用接口java.sql.Connection 。 但是,每个JDBC驱动程序都提供自己的Connection实现。 您不必知道特定实现的任何内容,因为它符合 Connection接口。

另一个来自java集合框架。 有一个java.util.Collection接口,它定义了sizeaddremove方法(等等)。 所以你可以交换使用所有types的集合。 假设你有以下几点:

 public float calculateCoefficient(Collection collection) { return collection.size() * something / somethingElse; } 

还有其他两个方法调用这个。 其他方法之一使用LinkedList因为它更有效,另一个使用TreeSet

由于LinkedListTreeSet实现了Collection接口,因此只能使用一种方法来进行系数计算。 不需要复制你的代码。

在这里,“程序到界面” – 你不关心size()方法到底有多精确,你知道它应该返回集合的大小 – 也就是说你已经编程到Collection接口,而不是LinkedListTreeSet

但是我的build议是find一个阅读材料 – 也许是一本书(例如“Java中的思维”) – 详细解释这个概念。

每个对象都有一个暴露的接口。 一个集合有AddRemoveAt等。一个套接字可以有SendReceiveClose等等。

你实际上可以得到一个引用的每个对象都有这些接口的具体实现。

这两件事情都很明显,但是有些不那么明显。

你的代码不应该依赖于一个对象的实现细节,只是它的发布的接口。

如果你把它变得极端,你只能对Collection<T>等(而不是ArrayList<T> )进行编码。 更实际一点,只要确保在不破坏代码的情况下,可以在概念上完全相同。

敲出Collection<T>例子:你有一些东西的集合,你实际上使用ArrayList<T>因为为什么不呢 。 如果将来最终使用LinkedList<T> ,则应确保代码不会中断。

这是一个devise场景,涉及到比萨饼:

http://fatagnus.com/program-to-an-interface-not-an-implementation/

它基本上意味着你将要使用的库的唯一部分应该依赖于它的API(应用程序编程接口),并且不应该将应用程序基于库的具体实现。

例如。 假设你有一个库给你一个stack 。 这个课程给你一些方法。 让我们说, pushpopisemptytop 。 你应该只依靠这些来编写你的应用程序。 一种违反这个的方法是偷看内部并发现堆栈是用某种数组实现的,所以如果你从一个空的堆栈中popup,你会得到某种索引exception,然后抓住这个而不是依靠类提供的isempty方法。 如果库提供程序从使用数组切换到使用某种列表,而后者仍然工作,假设提供者保持其API仍然工作,则前一种方法将失败。

“使用接口编程”发生在使用库时,其他代码依赖于您自己的代码。 然后,其他代码代表自己的方式,方法名称,参数,返回值等组成您必须编程的接口 。 所以这是关于如何使用第三方代码。

这也意味着,你不必关心你所依赖的代码的内部,只要界面保持不变,你的代码是安全的(或多或less…)

从技术上讲,有更好的细节,比如在Java中被称为“接口”的语言概念。

如果你想了解更多,你可以问什么“实现接口”是指…

我认为这是Erich Gamma的咒语之一。 我第一次没有find(在GOF书之前),但是你可以在访问中看到它的讨论: http : //www.artima.com/lejava/articles/designprinciples.html