接口允许您创建定义实现接口的类的方法的代码。但是,您不能向这些方法添加任何代码。

抽象类允许您做同样的事情,同时向方法添加代码。

现在,如果可以用抽象类实现同样的目标,我们为什么还需要接口的概念呢?

有人告诉我,这与从c++到Java的面向对象理论有关,这也是PHP面向对象的基础。这个概念在Java中有用而在PHP中没有吗?这仅仅是一种避免在抽象类中布满占位符的方法吗?我遗漏了什么吗?


当前回答

接口不会给您的代码带来任何性能提升或类似的东西,但它们可以使代码更易于维护。的确,抽象类(甚至非抽象类)可以用来建立到代码的接口,但是适当的接口(用关键字定义并且只包含方法签名的接口)更容易排序和读取。

话虽如此,在决定是否使用接口而不是类时,我倾向于谨慎。有时我想要默认的方法实现,或者所有子类都通用的变量。

当然,关于多接口实现的观点也是合理的。如果您有一个实现多个接口的类,您可以在同一个应用程序中使用该类的对象作为不同的类型。

The fact that your question is about PHP, though, makes things a bit more interesting. Typing to interfaces is still not incredibly necessary in PHP, where you can pretty much feed anything to any method, regardless of its type. You can statically type method parameters, but some of that is broken (String, I believe, causes some hiccups). Couple this with the fact that you can't type most other references, and there isn't much value in trying to force static typing in PHP (at this point). And because of that, the value of interfaces in PHP, at this point is far less than it is in more strongly-typed languages. They have the benefit of readability, but little else. Multiple-implementation isn't even beneficial, because you still have to declare the methods and give them bodies within the implementor.

其他回答

这个概念在面向对象编程中非常有用。对我来说,我认为接口是一种契约。只要我的类和你的类同意这个方法签名合同,我们就可以“接口”。至于抽象类,我认为更多的是存根一些方法的基类,我需要填充细节。

接口本质上是您可以创建的内容的蓝图。它们定义了类必须具有的方法,但是您可以在这些限制之外创建额外的方法。

我不确定你所说的不能向方法添加代码是什么意思——因为你可以。您是将接口应用到抽象类还是扩展它的类?

应用于抽象类的接口中的方法需要在该抽象类中实现。然而,将该接口应用于扩展类,该方法只需要在扩展类中实现。在这里我可能错了——我没有尽可能多地使用接口。

我一直认为接口是外部开发人员的一种模式,或者是确保事情正确的额外规则集。

接口不仅仅是为了确保开发人员实现特定的方法。其思想是,因为这些类保证有特定的方法,即使不知道类的实际类型,也可以使用这些方法。例子:

interface Readable {
  String read();
}

List<Readable> readables; // dunno what these actually are, but we know they have read();
for(Readable reader : readables)
  System.out.println(reader.read());

在许多情况下,提供基类是没有意义的,不管是不是抽象的,因为实现变化很大,除了一些方法之外没有任何共同之处。

动态类型语言有“鸭子类型”的概念,你不需要接口;您可以自由地假设对象具有您正在对其调用的方法。在静态类型语言中,对象有一些方法(在我的例子中是read()),但没有实现接口,这种方法可以解决这个问题。

接口的存在不是作为类可以扩展的基础,而是作为所需函数的映射。

下面是一个不适合抽象类的接口的例子: 假设我有一个日历应用程序,它允许用户从外部源导入日历数据。我将编写类来处理导入每种类型的数据源(ical, rss, atom, json),每个类都将实现一个公共接口,以确保它们都具有我的应用程序获取数据所需的公共方法。

<?php

interface ImportableFeed 
{
    public function getEvents();
}

Then when a user adds a new feed I can identify the type of feed it is and use the class developed for that type to import the data. Each class written to import data for a specific feed would have completely different code, there may otherwise be very few similarities between the classes outside of the fact that they are required to implement the interface that allows my application to consume them. If I were to use an abstract class, I could very easily ignore the fact that I have not overridden the getEvents() method which would then break my application in this instance whereas using an interface would not let my app run if ANY of the methods defined in the interface do not exist in the class that implemented it. My app doesn't have to care what class it uses to get data from a feed, only that the methods it needs to get that data are present.

To take this a step further, the interface proves to be extremely useful when I come back to my calendar app with the intent of adding another feed type. Using the ImportableFeed interface means I can continue adding more classes that import different feed types by simply adding new classes that implement this interface. This allows me to add tons of functionality without having to add unnecessarily bulk to my core application since my core application only relies on there being the public methods available that the interface requires so as long as my new feed import classes implement the ImportableFeed interface then I know I can just drop it in place and keep moving.

这只是一个非常简单的开始。然后,我可以创建另一个接口,所有日历类都需要实现该接口,该接口提供了更多特定于类处理的提要类型的功能。另一个很好的例子是验证提要类型的方法,等等。

This goes beyond the question but since I used the example above: Interfaces come with their own set of issues if used in this manner. I find myself needing to ensure the output that is returned from the methods implemented to match the interface and to achieve this I use an IDE that reads PHPDoc blocks and add the return type as a type hint in a PHPDoc block of the interface which will then translate to the concrete class that implements it. My classes that consume the data output from the classes that implement this interface will then at the very least know it's expecting an array returned in this example:

<?php
interface ImportableFeed 
{
    /**
     * @return array
     */
    public function getEvents();
}

没有太多空间来比较抽象类和接口。接口是简单的映射,在实现时要求类具有一组公共接口。

接口的全部意义在于为您提供灵活性,使您的类强制实现多个接口,但仍然不允许多重继承。从多个类继承的问题很多,维基百科的页面很好地总结了这些问题。

接口是一种折衷。关于多重继承的大多数问题并不适用于抽象基类,所以现在大多数现代语言禁用多重继承,但却调用抽象基类接口,并允许一个类“实现”任意数量的抽象基类。