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

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

如果你要这样做

IInterface classRef = new ObjectWhatever()

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

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


当前回答

它也适用于单元测试,你可以将你自己的类(满足接口的要求)注入到依赖它的类中

其他回答

编程到接口允许无缝地改变由接口定义的契约的实现。它允许契约和特定实现之间的松耦合。

IInterface classRef = new ObjectWhatever() 你可以使用任何实现IInterface的类吗?你什么时候需要这样做?

看看这个SE问题的好例子。

为什么应该首选Java类的接口?

使用接口会影响性能吗? 如果有,多少钱?

是的。它将在几秒内产生轻微的性能开销。但是如果您的应用程序需要动态地更改接口的实现,则不必担心性能影响。

如何避免这种情况而不需要维护两段代码呢?

如果您的应用程序需要多个接口实现,不要试图避免它们。在接口与某个特定实现缺乏紧密耦合的情况下,您可能必须部署补丁以将一个实现更改为另一个实现。

一个好的用例:Strategy模式的实现:

策略模式的真实例子

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

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

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

// 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());
    }
}

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

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.

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

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

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

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

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