代码之家  ›  专栏  ›  技术社区  ›  mk.

PHP中接口的意义是什么?

  •  209
  • mk.  · 技术社区  · 16 年前

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

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

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

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

    14 回复  |  直到 6 年前
        1
  •  136
  •   Mark Garcia    10 年前

    接口的整个要点是为您提供灵活性,让您的类强制实现多个接口,但仍然不允许多个继承。从多个类继承的问题是多方面的,并且 wikipedia 上面的一页很好地总结了它们。

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

        2
  •  120
  •   John Downey    16 年前

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

        3
  •  58
  •   tRuEsAtM    7 年前

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

    其中一个问题是:

    “钻石问题”(有时被称为“致命的钻石 死亡”)是当两个B类和C类继承时产生的歧义。 从A和类D继承自B和C。如果有方法 在A中,B和C已经覆盖,而D没有覆盖它,那么 D继承的方法版本是B还是C?

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

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

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

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

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

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

    您也可以在没有接口的情况下这样做,但是如果您使用接口,它是“更安全的”(因为它可以防止您犯错误)。该接口确保方法 创建() 将在实现接口的所有类中具有相同的签名(相同的类型和相同数量的参数)。这样就可以确保实现 IPersonService 接口,将有方法 创建() (在本例中)并且只需要1个参数( $人物对象 )呼叫/使用。

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

    我希望我不要重复太多。

        4
  •  22
  •   Sam McAfee    16 年前

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

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

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

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

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

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

        5
  •  20
  •   Ross    16 年前

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

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

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

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

        6
  •  10
  •   catalin.costache    11 年前

    您将在PHP中使用接口:

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

      类Car实现EngineInterface、BodyInterface、SteeringInterface{

    这样一个 Car 对象CA start() , stop() (发动机接口)或 goRight() , goLeft() (转向接口)

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

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

    从Java思维:

    一个接口说,这就是实现这个特定接口的所有类的外观。因此,任何使用特定接口的代码都知道可以为该接口调用什么方法,这就是全部。因此,该接口用于在类之间建立协议。

        7
  •  9
  •   Houghtelin    9 年前

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

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

    <?php
    
    interface ImportableFeed 
    {
        public function getEvents();
    }
    

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

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

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

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

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

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

        8
  •  6
  •   Tim Frey    16 年前

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

    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()),但不实现接口。

        9
  •  5
  •   Henrik Paul    16 年前

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

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

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

        10
  •  4
  •   Ross    16 年前

    我不记得PHP在这方面是不同的,但是在爪哇中,你可以实现多个接口,但是你不能继承多个抽象类。我假设PHP的工作方式相同。

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

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

        11
  •  3
  •   sergtk    16 年前

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

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

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

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

        12
  •  1
  •   vivek s vamja    8 年前

    下面是PHP接口的要点

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

    示例代码:

    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);
    
        13
  •  1
  •   Hiren Gohel PRASANNA KUMAR K G    7 年前

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

    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
    

    希望这能帮助任何人理解!

        14
  •  0
  •   Tapha    6 年前

    界面就像你的基因。

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

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