我曾多次看到有人提到这一点,但我不清楚这是什么意思。你什么时候,为什么要这么做?
我知道接口是做什么的,但我不清楚这一点的事实使我认为我错过了正确使用它们。
如果你要这样做
IInterface classRef = new ObjectWhatever()
你可以使用任何实现IInterface的类吗?你什么时候需要这样做?我能想到的唯一一件事是,如果你有一个方法,你不确定什么对象将被传递,除了它实现IInterface。我不知道你需要多久做一次。
另外,如何编写一个方法来接受实现接口的对象呢?这可能吗?
我曾经给学生的一个具体例子是他们应该写作
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();
你只需要在这一点上修改代码。您已经知道,其余的代码不会因为更改实现而被破坏,因为您对接口进行了编程。
当您谈论方法参数和返回值时,好处甚至更明显(我认为)。举个例子:
public ArrayList doSomething(HashMap map);
该方法声明将您绑定到两个具体实现(ArrayList和HashMap)。一旦从其他代码调用该方法,对这些类型的任何更改都可能意味着您将不得不更改调用代码。最好是根据接口进行编程。
public List doSomething(Map map);
现在,不管您返回什么样的List,或者作为参数传入什么样的Map。在doSomething方法中所做的更改不会强制您更改调用代码。
c++的解释。
把接口看作是类的公共方法。
然后你可以创建一个“依赖”于这些公共方法的模板来执行它自己的函数(它在类的公共接口中进行函数调用)。假设这个模板是一个容器,就像Vector类一样,它所依赖的接口是一个搜索算法。
任何定义函数/接口Vector调用的算法类都将满足“契约”(就像有人在原始回复中解释的那样)。算法甚至不需要是相同的基类;唯一的要求是向量所依赖的函数/方法(接口)在你的算法中定义。
所有这些的重点是,您可以提供任何不同的搜索算法/类,只要它提供了Vector所依赖的接口(冒泡搜索、顺序搜索、快速搜索)。
您可能还想设计其他容器(列表、队列),通过让它们实现搜索算法所依赖的接口/契约,来利用与Vector相同的搜索算法。
这节省了时间(OOP原则“代码重用”),因为你可以一次编写一个算法,而不是一遍又一遍地针对你创建的每个新对象,而不会因为过度生长的继承树而使问题过于复杂。
至于“错过”事情是如何运作的;这是非常重要的(至少在c++中),因为这是大多数标准模板库框架的运作方式。
当然,当使用继承和抽象类时,接口编程的方法会发生变化;但原理是一样的,你的公共函数/方法是你的类接口。
这是一个巨大的主题,也是设计模式的基石原则之一。
即使当我们不依赖于抽象时,面向接口编程也是有利的。
接口编程迫使我们使用对象的上下文适当的子集。这很有用,因为它:
防止我们做一些不合时宜的事,还有
让我们在将来安全地更改实现。
例如,考虑实现Friend和Employee接口的Person类。
class Person implements AbstractEmployee, AbstractFriend {
}
在这个人的生日的情况下,我们编程到朋友界面,以防止像对待员工一样对待这个人。
function party() {
const friend: Friend = new Person("Kathryn");
friend.HaveFun();
}
在这个人的工作环境中,我们对雇员界面进行编程,以防止模糊工作场所的边界。
function workplace() {
const employee: Employee = new Person("Kathryn");
employee.DoWork();
}
太好了。我们在不同的环境中都有适当的表现,我们的软件运行良好。
在遥远的未来,如果我们的业务改变为与狗打交道,我们可以相当容易地更改软件。首先,我们创建一个实现Friend和Employee的Dog类。然后,我们安全地将新的Person()更改为新的Dog()。即使两个函数都有数千行代码,这个简单的编辑也可以工作,因为我们知道以下是正确的:
Function party只使用Person的Friend子集。
函数workplace只使用Person的Employee子集。
类Dog实现了Friend和Employee接口。
另一方面,如果任何一方或工作场所都针对Person进行编程,就会有同时拥有特定于Person的代码的风险。从“人”改为“狗”需要我们梳理代码,删除任何“狗”不支持的“人”特定代码。
寓意:为接口编程可以帮助我们的代码适当地运行,并为更改做好准备。它还使我们的代码能够依赖于抽象,这带来了更多的好处。