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

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

如果你要这样做

IInterface classRef = new ObjectWhatever()

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

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


当前回答

Using interfaces is a key factor in making your code easily testable in addition to removing unnecessary couplings between your classes. By creating an interface that defines the operations on your class, you allow classes that want to use that functionality the ability to use it without depending on your implementing class directly. If later on you decide to change and use a different implementation, you need only change the part of the code where the implementation is instantiated. The rest of the code need not change because it depends on the interface, not the implementing class.

This is very useful in creating unit tests. In the class under test you have it depend on the interface and inject an instance of the interface into the class (or a factory that allows it to build instances of the interface as needed) via the constructor or a property settor. The class uses the provided (or created) interface in its methods. When you go to write your tests, you can mock or fake the interface and provide an interface that responds with data configured in your unit test. You can do this because your class under test deals only with the interface, not your concrete implementation. Any class implementing the interface, including your mock or fake class, will do.

编辑:下面是一篇文章的链接,其中Erich Gamma讨论了他的引用,“面向接口编程,而不是面向实现编程。”

http://www.artima.com/lejava/articles/designprinciples.html

其他回答

我坚信困难的问题应该用现实世界中简单的答案来解释。在软件设计领域,这是非常重要的。

看看你家里、学校、教堂的任何一扇门……任何建筑。

想象一下,有些门的底部有危险(所以你必须鞠躬才能与开门或关门的门互动),

或者其他人只能坐在左上角(所以,一些侏儒、残疾人或凯文·哈特不会觉得这种门很有趣,也不会觉得它很好用)。

所以设计是关键,创建程序让其他人可以开发/使用它。

接口的作用是让大型项目中的初级/高级开发人员更容易工作,这样每个人都知道自己在做什么,而不需要别人的帮助,这样你就可以尽可能顺利地工作(理论上)。


[1]:如何?通过揭示价值的形态。所以,你不需要文档,因为代码本身是不言自明的(棒极了)。


这个答案并不是针对特定语言的,而是概念驱动的(毕竟,人类是通过编写代码来创建工具的)。

听起来好像你知道接口是如何工作的,但不确定何时使用它们以及它们提供了什么优势。下面是几个例子,说明界面是有意义的:

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

大多数外接程序和插件系统都离不开接口。

另一种流行的用法是Repository模式。假设我想从不同的来源加载一个邮政编码列表

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

然后我可以创建一个XMLZipCodeRepository, SQLZipCodeRepository, CSVZipCodeRepository等。对于我的web应用程序,我经常在早期创建XML存储库,这样我就可以在SQL数据库准备好之前启动并运行一些东西。一旦数据库准备好了,我就编写一个SQLRepository来替换XML版本。我的其余代码保持不变,因为它完全基于接口运行。

方法可以接受如下接口:

PrintZipCodes(IZipCodeRepository zipCodeRepository, string state)
{
    foreach (ZipCode zipCode in zipCodeRepository.GetZipCodes(state))
    {
        Console.WriteLine(zipCode.ToString());
    }
}

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

一个对象的操作所定义的所有签名的集合称为该对象的接口

输入n.指定接口

上面定义的接口的一个简单例子是所有PDO对象方法,如query()、commit()、close()等,作为一个整体,而不是分开。这些方法,即它的接口定义了可以发送到对象的完整消息和请求集。

上面定义的类型是一个特定的接口。我将使用创建的形状界面来演示:draw(), getArea(), getPerimeter()等。

如果一个对象是Database类型的,我们的意思是它接受数据库接口、query()、commit()等的消息/请求。对象可以有多种类型。只要实现了接口,就可以让数据库对象具有形状类型,在这种情况下,这将是子类型。

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

客户机将只知道接口,而不知道实现。

因此,从本质上讲,编程到一个接口将涉及到一些类型的抽象类,如只指定接口的Shape,即draw(), getCoordinates(), getArea()等。然后让不同的具体类实现这些接口,比如圆形类,方形类,三角形类。因此,针对接口而不是实现编程。

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

例如,我可以有一个移动界面。一个方法可以让一些东西“移动”,任何实现移动接口的对象(Person, Car, Cat)都可以被传递进来并被告知移动。如果每个方法都不知道类的类型,那么它就是类。

即使当我们不依赖于抽象时,面向接口编程也是有利的。

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

防止我们做一些不合时宜的事,还有 让我们在将来安全地更改实现。

例如,考虑实现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的代码的风险。从“人”改为“狗”需要我们梳理代码,删除任何“狗”不支持的“人”特定代码。

寓意:为接口编程可以帮助我们的代码适当地运行,并为更改做好准备。它还使我们的代码能够依赖于抽象,这带来了更多的好处。