关于oop:PHP中接口的意义是什么?

What is the point of interfaces in PHP?

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

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

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

有人告诉我,它与OO理论有关,从C++到Java,这是PHP的面向对象的东西。这个概念在Java中有用,但在PHP中不是有用的吗?这只是一种避免在抽象类中放置占位符的方法吗?我错过什么了吗?


接口的整个要点是为您提供灵活性,让您的类强制实现多个接口,但仍然不允许多个继承。从多个类继承的问题是多方面的,并且在维基百科页面上可以很好地总结这些问题。

接口是一种妥协。多继承的大多数问题都不适用于抽象基类,因此现在大多数现代语言都禁用多继承,但调用抽象基类接口,并允许类根据需要"实现"任意多个继承。


这个概念在面向对象的编程中到处都是有用的。在我看来,界面是一种契约。只要我班和你们班在这个方法签名合同上达成一致,我们就可以"接口"。至于抽象类,我认为这些类更多的是一些基本类,它们扼杀了一些方法,我需要填写详细信息。


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

其中一个问题是:

The"diamond problem" (sometimes referred to as the"deadly diamond of
death") is an ambiguity that arises when two classes B and C inherit
from A and class D inherits from both B and C. If there is a method
in A that B and C have overridden, and D does not override it, then
which version of the method does D inherit: that of B, or that of C?

资料来源:https://en.wikipedia.org/wiki/multiple_inheritance The_diamond_problem

为什么/何时使用接口?一个例子…世界上所有的汽车都有相同的接口(方法)。AccelerationPedalIsOnTheRight()BrakePedalISOnTheLeft()。想象一下,每个汽车品牌都有不同于其他品牌的这些"方法"。宝马的右边是刹车,本田的左边是刹车。人们每次购买不同品牌的汽车时,都必须学习这些"方法"是如何工作的。这就是为什么在多个"地方"有相同的接口是个好主意。

接口对您有什么作用(为什么有人会使用它)?接口防止您犯"错误"(它确保实现特定接口的所有类都具有接口中的方法)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 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类。我们使用方法的方式保持不变(接口保持不变)。

例如,它将像这样使用(在我们的代码中的任何地方):

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

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

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

与多个不同的接口相比,记住一个接口并在任何地方使用同一个接口要容易得多。

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

您也可以在没有接口的情况下这样做,但是如果您使用接口,它是"更安全的"(因为它可以防止您犯错误)。接口保证方法Create()在实现接口的所有类中具有相同的签名(相同的类型和相同数量的参数)。这样,您就可以确保实现IPersonService接口的任何类都具有方法Create()(在本例中),并且只需要1个参数($personObject即可被调用/使用。

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

我希望我不要重复太多。


对于我来说,使用接口和抽象类的区别更多地与代码组织有关,而不是由语言本身实现。我在为其他开发人员准备代码时经常使用它们,以便它们保持在预期的设计模式中。接口是一种"契约式设计",即您的代码同意响应一组指定的API调用,这些调用可能来自您不熟悉的代码。

虽然抽象类的继承是一个"是"关系,但这并不总是您想要的,实现接口更像是一个"行为类似"关系。在某些情况下,这种差异可能非常显著。

例如,假设您有一个抽象类帐户,许多其他类从该帐户扩展(帐户类型等)。它有一组特定的方法,这些方法只适用于该类型组。但是,这些帐户子类中的一些实现了可版本化、可列表化或可编辑,以便将它们抛出到期望使用这些API的控制器中。控制器不关心它是什么类型的对象

相比之下,我还可以创建一个不从帐户扩展的对象,比如用户抽象类,并且仍然实现可列出和可编辑,但不可版本化,这在这里没有意义。

通过这种方式,我要说的是,fooser子类不是一个帐户,而是一个可编辑的对象。同样,baraccount从account扩展,但不是用户子类,而是实现可编辑、可列出和可版本控制。

将所有这些可编辑的、可列出的和可版本化的API添加到抽象类本身不仅会变得杂乱和丑陋,而且会复制帐户和用户中的公共接口,或者强制我的用户对象实现可版本化,可能只是抛出一个异常。


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

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

应用于抽象类的接口中的方法需要在该抽象类中实现。但是,将该接口应用于扩展类,并且该方法只需要在扩展类中实现。在这里我可能是错的-我没有尽可能频繁地使用接口。

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


您将在PHP中使用接口:

  • 隐藏实现——建立一个对象类的访问协议,在使用该对象的所有位置更改底层实现,而不进行重构。
  • 检查类型,如确保参数具有特定的类型$object instanceof MyInterface
  • 在运行时强制参数检查
  • 将多个行为实现到一个类中(构建复杂类型)


    class Car implements EngineInterface, BodyInterface, SteeringInterface {

  • so that a Car object ca now start(), stop() (EngineInterface) or goRight(),goLeft() (Steering interface)

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

    第四,它可能是最明显的用例,您不能用抽象类来处理它。

    从Java思维:

    An interface says,"This is what all classes that implement this particular interface will look like." Thus, any code that uses a particular interface knows what methods can be called for that interface, and that’s all. So the interface is used to establish a"protocol" between classes.


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

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

    1
    2
    3
    4
    5
    6
    <?php

    interface ImportableFeed
    {
        public function getEvents();
    }

    然后,当用户添加一个新的提要时,我可以识别它是什么类型的提要,并使用为该类型开发的类来导入数据。为特定提要导入数据而编写的每个类都将具有完全不同的代码,否则,在实现允许应用程序使用这些类的接口所需的类之外,这些类之间的相似性可能非常少。如果要使用抽象类,我可以很容易地忽略这样一个事实:我没有重写GetEvents()方法,该方法将在本实例中破坏我的应用程序,而使用接口将不会让我的应用程序运行,如果接口中定义的任何方法在实现它的类中都不存在。我的应用程序不需要关心它使用什么类从提要中获取数据,只需要知道它需要的方法已经存在。

    为了更进一步,当我回到我的日历应用程序并打算添加另一种提要类型时,这个界面被证明是非常有用的。使用importablefeed接口意味着我可以通过简单地添加实现此接口的新类来继续添加更多导入不同feed类型的类。这允许我添加大量功能,而不必向核心应用程序添加不必要的批量,因为我的核心应用程序只依赖于存在接口所需的公共方法,只要我的新feed导入类实现了importable feed接口,那么我知道我可以将其放在适当的位置,并保持VEN。

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

    这超出了问题的范围,但由于我使用了上面的示例:如果以这种方式使用,接口会有自己的一组问题。我发现自己需要确保从实现的方法返回的输出与接口匹配,为了实现这一点,我使用一个读取phpdoc块的IDE,并将返回类型作为类型提示添加到接口的phpdoc块中,然后将其转换为实现它的具体类。使用实现此接口的类的数据输出的类将至少知道它期望在此示例中返回一个数组:

    1
    2
    3
    4
    5
    6
    7
    8
    <?php
    interface ImportableFeed
    {
        /**
         * @return array
         */

        public function getEvents();
    }

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


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

    1
    2
    3
    4
    5
    6
    7
    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());

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

    动态类型语言的概念是"duck-typing",在这里您不需要接口;您可以自由地假设对象具有您调用的方法。这可以解决静态类型语言中的问题,其中对象有一些方法(在我的示例中,read()),但不实现接口。


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

    与Java相比,接口是不实用的/有意义的。另一方面,php6将引入更多的类型提示,包括返回值的类型提示。这应该为PHP接口增加一些价值。

    tl;dr:interfaces定义了需要遵循的方法列表(think api),而抽象类提供了一些基本/通用的功能,这些子类根据特定的需要进行改进。


    I can't remember if PHP is different in this respect, but in Java, you can implement multiple Interfaces, but you can't inherit multiple abstract classes. I'd assume PHP works the same way.

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

    对于多个抽象类,您可以让多个抽象相互扩展(同样,我不完全确定这一点,但我想我以前在某个地方见过)。唯一不能扩展的是最后一个类。


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

    也就是说,在决定是否在类上使用接口时,我倾向于使用谨慎性。有时我需要默认的方法实现,或者所有子类都通用的变量。

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

    不过,您的问题是关于PHP的,这使得事情变得更加有趣。在PHP中,输入接口仍然不是非常必要的,在那里你几乎可以向任何方法提供任何东西,不管它的类型如何。您可以静态地输入方法参数,但其中一些参数被破坏(我相信,字符串会导致一些打嗝)。结合这一点,您不能键入大多数其他引用,并且在尝试用PHP强制静态键入时没有太大的价值(此时)。正因为如此,PHP中的接口的价值,在这一点上远低于它在更强类型语言中的价值。它们有可读性的好处,但其他的好处不大。多个实现甚至都没有好处,因为您仍然需要声明这些方法,并在实现者中为它们提供实体。


    界面就像你的基因。

    抽象类就像你真正的父母。

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


    我不知道其他语言,那里的接口概念是什么。但是对于php,我会尽我最大的努力来解释它。耐心一点,如果这有帮助,请发表评论。

    An interface works as a"contracts", specifying what a set of subclasses does, but not how they do it.

    规则

  • 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.
  • 现在让我们举个例子。假设我们有两个玩具:一个是狗,另一个是猫。

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

    When the user presses speak button, the toy have to speak it doesn't matter if it's Dog or a Cat.

    这是使用接口而不是抽象类的一个很好的例子,因为实现是不同的。为什么?记得

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


    我们看到抽象类和接口类似,因为它们提供必须在子类中实现的抽象方法。但是,它们仍然存在以下差异:

    1.Interfaces can include abstract methods and constants, but cannot contain concrete methods and variables.

    2.All the methods in the interface must be in the public visibility
    scope.

    3.A class can implement more than one interface, while it can inherit
    from only one abstract class.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
                                      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接口的要点

  • 它用于定义类中所需的方法数[如果要加载HTML,则需要ID和名称,因此在本例中,接口包括setid和setname]。
  • 接口严格地强制类包含其中定义的所有方法。
  • 只能在具有公共可访问性的接口中定义方法。
  • 您还可以像类一样扩展接口。可以使用extends关键字在PHP中扩展接口。
  • 扩展多个接口。
  • 如果两个接口共享同名的函数,则不能实现两个接口。它会抛出错误。
  • 示例代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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);