Java &引用;编程到一个接口";。这是什么意思?

Java &引用;编程到一个接口";。这是什么意思?,java,oop,Java,Oop,可能重复: 我经常遇到这个词: 编程到一个接口 这到底是什么意思?一个真实的设计场景将受到高度赞赏。我认为这是Erich Gamma的咒语之一。我找不到他第一次描述它(在GOF书之前),但你可以在采访中看到它的讨论:“编程到接口”发生在你使用库时,你在自己的代码中依赖的其他代码。然后,其他代码向您表示自身的方式、方法名称、参数、返回值等构成了您必须编程的接口。这是关于如何使用第三方代码的 这也意味着,您不必关心所依赖的代码的内部结构,只要接口保持不变,您的代码就是安全的(好吧,或多或少…) 从

可能重复:

我经常遇到这个词:

编程到一个接口


这到底是什么意思?一个真实的设计场景将受到高度赞赏。

我认为这是Erich Gamma的咒语之一。我找不到他第一次描述它(在GOF书之前),但你可以在采访中看到它的讨论:

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

这也意味着,您不必关心所依赖的代码的内部结构,只要接口保持不变,您的代码就是安全的(好吧,或多或少…)

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


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

现实世界的例子很实用。其中一项:

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

另一个来自java集合框架。有一个
java.util.Collection
接口,它定义了
size
add
remove
方法(以及许多其他方法)。因此,您可以互换地使用所有类型的集合。假设你有以下几点:

public float calculateCoefficient(Collection collection) {
    return collection.size() * something / somethingElse;
}
还有另外两个调用这个的方法。另一种方法使用
LinkedList
,因为它更高效,而另一种方法使用
TreeSet

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

这里是“编程到接口”-您不关心
size()
方法的具体实现方式,您知道它应该返回集合的大小-也就是说,您已经编程到
集合
接口,而不是
链接列表
树集


但是我的建议是找一本书来阅读——比如说一本书(“用Java思考”),书中详细解释了这个概念。

每个对象都有一个公开的接口。集合有
添加
删除
等。套接字可能有
发送
接收
关闭

实际上,每个可以获得引用的对象都有这些接口的具体实现

这两件事都是显而易见的,但不太明显的是

您的代码不应依赖于对象的实现细节,而应依赖于其发布的接口。

如果你把它发挥到极致,你只需要针对
Collection
等进行编码(而不是
ArrayList
)。更实际地说,只需确保可以在不破坏代码的情况下交换概念上相同的内容


要敲出
集合
示例:您有一个某物的集合,实际上您正在使用
ArrayList
,因为为什么不这样做。如果将来使用
LinkedList
,你应该确保你的代码不会被破坏。

这基本上意味着你要使用的库中唯一应该依赖的部分是它的API(应用程序编程接口)您不应该将应用程序建立在库的具体实现之上


假设你有一个能给你一个
堆栈的库。这个类提供了几个方法。比如说
push
pop
isempty
top
。您应该仅依靠这些来编写应用程序。违反这一点的一种方法是查看内部,发现堆栈是使用某种数组实现的,这样,如果从空堆栈中弹出,就会得到某种索引异常,然后捕获它,而不是依赖类提供的
isempty
方法。如果库提供者从使用数组切换到使用某种列表,而后者仍然工作,假设提供者保持API仍在工作,则前者的方法将失败

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

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

  • 只要对象符合客户机期望的接口,客户机就不知道他们使用的对象的特定类型
  • 客户端仍然不知道实现这些对象的类。客户端只知道定义接口的抽象类
  • 这大大减少了子系统之间的实现依赖性,从而产生了对接口进行编程的原则

    有关此设计的进一步说明,请参见

    资料来源:G.O.F


    另请参见:

    ,简单地说,不要以这样的方式编写类

    我依靠这个班来完成我的工作

    你写的方式是这样的

    我依靠任何做这些事情的班级来完成我的工作

    第一个示例表示一个依赖于特定具体实现来完成其工作的类。从本质上来说,这不是很灵活

    第二个示例表示写入接口的类。它不关心你使用什么具体的对象,它只关心它的实现
    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("..."); }
    }
    
    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());
    
    for(IDrivable vehicle: vehicleList) 
    {
      vehicle.accelerate(); //this could be a bike or a car, or anything that implements IDrivable
    }