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

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

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

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


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


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

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

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

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


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

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


在我看来,接口应该优于非函数抽象类。如果性能受到影响,我也不会感到惊讶,因为只实例化了一个对象,而不是解析两个对象,并将它们组合在一起(尽管,我不能确定,我不熟悉OOP PHP的内部工作原理)。

的确,与Java相比,接口没有那么有用/有意义。另一方面,PHP6将引入更多的类型提示,包括返回值的类型提示。这将为PHP接口增加一些价值。

interfaces定义了需要遵循的方法列表(想想API),而抽象类提供了一些基本/通用的功能,这些功能由子类细化以满足特定需求。


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

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()),但没有实现接口,这种方法可以解决这个问题。


我不记得PHP在这方面是否有所不同,但在Java中,您可以实现多个接口,但不能继承多个抽象类。我认为PHP也是这样工作的。

在PHP中,您可以通过用逗号分隔多个接口来应用它们(我认为,我不认为这是一个干净的解决方案)。

至于多个抽象类,你可以有多个相互扩展的抽象(同样,我不完全确定,但我想我以前在某个地方见过)。唯一不能扩展的是final类。


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

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

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

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.


对我来说,使用接口和抽象类之间的区别更多地与代码组织有关,而不是由语言本身强制执行。在为其他开发人员准备代码时,我经常使用它们,以便它们保持在预期的设计模式内。接口是一种“契约式设计”,您的代码同意响应一组规定的API调用,这些API调用可能来自您没有访问权限的代码。

虽然从抽象类继承是一个“是一个”关系,但这并不总是你想要的,实现接口更像是一个“像一个”关系。在某些情况下,这种差异可能相当显著。

例如,假设您有一个抽象类Account,许多其他类都是从这个抽象类扩展而来的(Account的类型等等)。它有一组特定的方法,只适用于该类型组。但是,这些帐户子类中的一些实现了Versionable、Listable或Editable,以便它们可以被扔到期望使用这些api的控制器中。控制器并不关心对象的类型

相比之下,我还可以创建一个不从Account扩展的对象,比如User抽象类,仍然实现Listable和Editable,但不实现Versionable,这在这里没有意义。

通过这种方式,我说FooUser子类不是一个帐户,但确实像一个可编辑对象。同样,BarAccount从Account扩展而来,但不是User子类,而是实现了可编辑、可列表和可版本。

将所有这些可编辑的、可列表的和可版本的api添加到抽象类本身不仅是混乱和丑陋的,而且会重复Account和User中的公共接口,或者强制我的User对象实现Versionable,可能只是抛出一个异常。


你将在PHP中使用接口:

隐藏实现——建立一个对对象类的访问协议,改变底层实现,而不需要在所有使用过对象的地方进行重构 检查类型-例如确保参数具有特定类型$object instanceof MyInterface 在运行时强制参数检查 要在单个类中实现多个行为(构建复杂类型)

类Car实现EngineInterface, BodyInterface, steinginterface { 这样Car对象现在可以start(), stop() (EngineInterface)或goRight(),goLeft()(转向接口)

还有其他我现在想不起来的事情

第四点,这可能是你不能用抽象类解决的最明显的用例。

来自《Java思维》:

接口表示:“这是实现这个特定接口的所有类的样子。”因此,任何使用特定接口的代码都知道可以为该接口调用哪些方法,仅此而已。因此,接口用于在类之间建立“协议”。


如果已经有抽象类了,为什么还需要接口呢? 防止多重继承(可能导致多个已知问题)。

这类问题之一:

“钻石问题”(有时被称为“致命的钻石”) 死亡”)是当两个类B和C继承时产生的模糊性 类D继承自类B和类c B和C已经覆盖了A, D没有覆盖它,那么 D继承了哪个版本的方法:B的还是C的?

来源:https://en.wikipedia.org/wiki/Multiple_inheritance The_diamond_problem

Why/When to use an interface? An example... All cars in the world have the same interface (methods)... AccelerationPedalIsOnTheRight(), BrakePedalISOnTheLeft(). Imagine that each car brand would have these "methods" different from another brand. BMW would have The brakes on the right side, and Honda would have brakes on the left side of the wheel. People would have to learn how these "methods" work every time they would buy a different brand of car. That's why it's a good idea to have the same interface in multiple "places."

界面为你做了什么(为什么有人会使用它)? 接口可以防止你犯“错误”(它保证所有实现特定接口的类都有接口中的方法)。

// Methods inside this interface must be implemented in all classes which implement this interface.
interface IPersonService
{   
    public function Create($personObject);
}

class MySqlPerson implements IPersonService
{
    public function Create($personObject)
    {
        // Create a new person in MySql database.
    }
}

class MongoPerson implements IPersonService
{
    public function Create($personObject)
    {
        // Mongo database creates a new person differently then MySQL does. But the code outside of this method doesn't care how a person will be added to the database, all it has to know is that the method Create() has 1 parameter (the person object).
    }
}

这样,Create()方法将始终以相同的方式使用。不管我们使用的是MySqlPerson类还是MongoPerson类。我们使用方法的方式保持不变(接口保持不变)。

例如,它将像这样使用(在我们的代码中无处不在):

new MySqlPerson()->Create($personObject);
new MongoPerson()->Create($personObject);

这样,这样的事情就不会发生:

new MySqlPerson()->Create($personObject)
new MongoPerson()->Create($personsName, $personsAge);

记住一个界面并在任何地方使用相同的界面要比多个不同的界面容易得多。

这样,Create()方法的内部对于不同的类可以是不同的,而不会影响调用该方法的“外部”代码。所有外部代码必须知道的是Create()方法有1个参数($personObject),因为这是外部代码使用/调用该方法的方式。外部代码并不关心方法内部发生了什么;它只需要知道如何使用/调用它。

You can do this without an interface as well, but if you use an interface, it's "safer" (because it prevents you to make mistakes). The interface assures you that the method Create() will have the same signature (same types and a same number of parameters) in all classes that implement the interface. This way you can be sure that ANY class which implements the IPersonService interface, will have the method Create() (in this example) and will need only 1 parameter ($personObject) to get called/used.

实现接口的类必须实现接口所具有的所有方法。

我希望我没有重复太多。


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

下面是一个不适合抽象类的接口的例子: 假设我有一个日历应用程序,它允许用户从外部源导入日历数据。我将编写类来处理导入每种类型的数据源(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();
}

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


下面是PHP接口的要点

It is used to define required no of methods in class [if you want to load html then id and name is required so in this case interface include setID and setName]. Interface strictly force class to include all the methods define in it. You can only define method in interface with public accessibility. You can also extend interface like class. You can extend interface in php using extends keyword. Extend multiple interface. You can not implement 2 interfaces if both share function with same name. It will throw error.

示例代码:

interface test{
    public function A($i);
    public function B($j = 20);
}

class xyz implements test{
    public function A($a){
        echo "CLASS A Value is ".$a;
    }
    public function B($b){
        echo "CLASS B Value is ".$b;
    }
}
$x = new xyz();
echo $x->A(11);
echo "<br/>";
echo $x->B(10);

我们看到抽象类和接口的相似之处在于它们提供了必须在子类中实现的抽象方法。但是,二者仍有以下差异:

1.接口可以包含抽象方法和常量,但不能包含具体方法和变量。 2.接口中的所有方法都必须在公共可见性中 范围。 3.一个类可以实现多个接口,同时它可以继承 只来自一个抽象类。

                                  interface                      abstract class
the code                     - abstract methods               - abstract methods
                             - constants                      - constants                  
                                                              - concrete methods
                                                              - concrete variables

access modifiers             
                             - public                         - public
                                                              - protected
                                                              - private
                                                                etc.
number of parents          The same class can implement
                           more than 1 interface              The child class can 
                                                              inherit only from 1 abstract class

希望这将有助于任何人理解!


界面就像你的基因。

抽象类就像你实际的父母。

它们的目的是遗传的,但是在抽象类和接口的情况下,继承的是更具体的。


我不了解其他语言,界面的概念是什么。但对于PHP,我将尽力解释。耐心点,如果有帮助请评论。

接口就像“契约”一样工作,指定一组子类做什么,但不指定它们如何做。

规则

An Interface can't be instantiate. You can't implement any method in an interface,i.e. it only contains .signature of the method but not details(body). Interfaces can contain methods and/or constants, but no attributes. Interface constants have the same restrictions as class constants. Interface methods are implicitly abstract. Interfaces must not declare constructors or destructors, since these are implementation details on the class level. All the methods in an interface must have public visibility.

现在让我们举个例子。 假设我们有两个玩具:一个是狗,另一个是猫。

正如我们所知,狗叫,猫叫。这两者具有相同的说话方法,但具有不同的功能或实现。 假设我们给用户一个带有说话按钮的遥控器。

当用户按下说话按钮时,玩具必须说话,无论它是狗还是猫。

这是使用接口而不是抽象类的好情况,因为实现是不同的。 为什么?还记得

如果您需要通过添加一些非抽象方法来支持子类,则应该使用抽象类。否则,接口将是您的选择。