我曾多次看到有人提到这一点,但我不清楚这是什么意思。你什么时候,为什么要这么做?

我知道接口是做什么的,但我不清楚这一点的事实使我认为我错过了正确使用它们。

如果你要这样做

IInterface classRef = new ObjectWhatever()

你可以使用任何实现IInterface的类吗?你什么时候需要这样做?我能想到的唯一一件事是,如果你有一个方法,你不确定什么对象将被传递,除了它实现IInterface。我不知道你需要多久做一次。

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


当前回答

前面的回答主要关注为了可扩展性和松耦合而对抽象进行编程。虽然这些都很重要, 可读性同样重要。可读性允许其他人(以及您未来的自己)以最小的努力理解代码。这就是可读性利用抽象的原因。

根据定义,抽象比实现更简单。抽象省略了细节以传达事物的本质或目的,仅此而已。 由于抽象更简单,与实现相比,我可以一次在脑海中容纳更多的抽象。

作为一名程序员(使用任何语言),我的脑海中始终有一个List的大致概念。特别是,List允许随机访问、重复元素并保持顺序。当我看到这样的声明:List myList = new ArrayList()我想,很酷,这是一个以我理解的(基本)方式使用的List;我就不用再想了

On the other hand, I do not carry around the specific implementation details of ArrayList in my head. So when I see, ArrayList myList = new ArrayList(). I think, uh-oh, this ArrayList must be used in a way that isn't covered by the List interface. Now I have to track down all the usages of this ArrayList to understand why, because otherwise I won't be able to fully understand this code. It gets even more confusing when I discover that 100% of the usages of this ArrayList do conform to the List interface. Then I'm left wondering... was there some code relying on ArrayList implementation details that got deleted? Was the programmer who instantiated it just incompetent? Is this application locked into that specific implementation in some way at runtime? A way that I don't understand?

我现在对这个应用程序感到困惑和不确定,我们所讨论的只是一个简单的List。如果这是一个忽略其接口的复杂业务对象呢?那么我的业务领域知识不足以理解代码的目的。

因此,即使当我在私有方法中严格需要List时(如果它改变了,不会破坏其他应用程序,并且我可以很容易地找到/替换IDE中的每个用法),它仍然有利于编程到抽象的可读性。因为抽象比实现细节更简单。您可以说,对抽象进行编程是遵循KISS原则的一种方式。

其他回答

为一个接口编程就是在说:“我需要这个功能,我不在乎它来自哪里。”

Consider (in Java), the List interface versus the ArrayList and LinkedList concrete classes. If all I care about is that I have a data structure containing multiple data items that I should access via iteration, I'd pick a List (and that's 99% of the time). If I know that I need constant-time insert/delete from either end of the list, I might pick the LinkedList concrete implementation (or more likely, use the Queue interface). If I know I need random access by index, I'd pick the ArrayList concrete class.

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

我从事与运行医疗设备的服务器对话的客户端软件工作。我们正在开发这种设备的新版本,其中有一些新组件,客户必须不时进行配置。有两种类型的新组件,它们是不同的,但也非常相似。基本上,我必须创建两个配置表单,两个列表类,所有东西都要创建两个。

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

我为这些组件定义了一个简单的接口,所有基类都与该接口对话。现在当我改变一些东西,它几乎“只是工作”在任何地方,我没有代码复制。

c++的解释。

把接口看作是类的公共方法。

然后你可以创建一个“依赖”于这些公共方法的模板来执行它自己的函数(它在类的公共接口中进行函数调用)。假设这个模板是一个容器,就像Vector类一样,它所依赖的接口是一个搜索算法。

任何定义函数/接口Vector调用的算法类都将满足“契约”(就像有人在原始回复中解释的那样)。算法甚至不需要是相同的基类;唯一的要求是向量所依赖的函数/方法(接口)在你的算法中定义。

所有这些的重点是,您可以提供任何不同的搜索算法/类,只要它提供了Vector所依赖的接口(冒泡搜索、顺序搜索、快速搜索)。

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

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

至于“错过”事情是如何运作的;这是非常重要的(至少在c++中),因为这是大多数标准模板库框架的运作方式。

当然,当使用继承和抽象类时,接口编程的方法会发生变化;但原理是一样的,你的公共函数/方法是你的类接口。

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

接口类似于契约,您希望实现类实现在契约(接口)中编写的方法。由于Java不提供多重继承,“编程到接口”是实现多重继承的好方法。

如果您有一个类a,它已经扩展了其他一些类B,但是您希望这个类a也遵循某些指导方针或实现某个契约,那么您可以通过“编程到接口”策略来实现。

有很多解释,但让它更简单。以List为例。可以使用as实现一个列表:

内部数组 链表 其他实现

通过构建一个接口,比如一个List。您只编写了List的定义或List在实际中的含义。

你可以在内部使用任何类型的实现比如数组实现。但是假设您希望由于某种原因(比如bug或性能)更改实现。然后你只需要改变声明List<String> ls = new ArrayList<String>()为List<String> ls = new LinkedList<String>()。

在代码的其他地方,你不需要改变任何东西;因为其他所有东西都建立在List的定义之上。