PHP 中接口的意义是什么?

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

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

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

有人告诉我,这与从 C + + 到 Java 的面向对象理论有关,而这正是 PHP 的面向对象技术所基于的。这个概念在 Java 中有用,在 PHP 中没用吗?这只是一种避免占位符在抽象类中乱丢的方法吗?我错过了什么吗?

100801 次浏览

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

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

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

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

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

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

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

在我看来,接口应该优于非函数抽象类。如果性能受到影响,我也不会感到惊讶,因为只实例化了一个对象,而不是解析两个对象,并将它们组合在一起(尽管,我不能确定,我不熟悉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类。

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

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

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

不过,您的问题是关于PHP的,这让事情变得更有趣了一些。在PHP中,输入到接口仍然不是非常必要的,在PHP中,几乎可以将任何东西输入到任何方法,而不管其类型如何。您可以静态地输入方法参数,但其中一些是坏的(我认为,字符串会导致一些问题)。加上不能键入大多数其他引用的事实,在PHP (此时此刻)中强制执行静态类型并没有多大价值。正因为如此,接口在PHP中此时此刻的值远小于更强类型语言中的值。它们有可读性的好处,但仅此而已。多重实现甚至没有好处,因为您仍然必须声明方法并在实现者中为它们提供主体。

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

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

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

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

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

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

你将在PHP中使用接口:

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

    类Car实现EngineInterface, BodyInterface, steinginterface {

  • 因此,Car对象现在可以start()stop() (EngineInterface)或goRight()goLeft() (Steering interface)

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

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

    来自《Java思维》:

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

    如果已经有抽象类了,为什么还需要接口呢?

    .防止多重继承(可能导致多个已知问题)

    这类问题之一:

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

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

    为什么/什么时候使用接口? 一个例子……世界上所有的汽车都有相同的接口(方法)…AccelerationPedalIsOnTheRight() BrakePedalISOnTheLeft()。想象一下,每个汽车品牌都有这些不同于其他品牌的“方法”。宝马会把刹车安装在右边,本田会把刹车安装在左边。人们每次购买不同品牌的汽车时,都必须学习这些“方法”是如何工作的。这就是为什么在多个“地方”使用相同的界面是个好主意。" < / p > 接口为你做了什么(为什么有人会使用它)? 接口可以防止你犯“错误”(它保证所有实现特定接口的类都有接口中的方法)

    // 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()有一个参数($personObject),因为这是外部代码使用/调用该方法的方式。外部代码并不关心方法内部发生了什么;它只需要知道如何使用/调用它。

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

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

    我希望我没有重复太多。

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

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

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

    然后,当用户添加一个新的提要时,我可以识别它是提要的类型,并使用为该类型开发的类来导入数据。为特定提要导入数据而编写的每个类都有完全不同的代码,除了需要实现允许应用程序使用它们的接口之外,类之间可能很少有相似之处。如果我要使用一个抽象类,我可以很容易地忽略这样一个事实,我没有覆盖getEvents()方法,然后在这个实例中打破我的应用程序,而使用接口不会让我的应用程序运行,如果接口中定义的任何方法都不存在于实现它的类中。我的应用程序不需要关心它使用什么类来从提要中获取数据,只需要关心它获得数据所需的方法。

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

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

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

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

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

    下面是PHP接口的要点

    1. 它用于定义类中的required no of方法[如果你想加载html,那么id和name是必需的,所以在这种情况下接口包括setID和setName]。
    2. 接口严格强制类包含其中定义的所有方法。
    3. 只能在具有公共可访问性的接口中定义方法。
    4. 你也可以像类一样扩展接口。你可以在php中使用extends关键字扩展接口。
    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);
    

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

    1.接口可以包含抽象方法和常量,但不能包含具体方法和变量。

    < p > 2。接口中的所有方法必须在公共可见性中 范围。< / p > < p > 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,我将尽力解释。耐心点,如果有帮助请评论。

    接口以“契约”的方式工作,指定一组子类做什么,但不指定它们如何做。

    规则

    1. 接口不能实例化。

    2. 你不能在接口中实现任何方法,例如:它只包含方法的.signature,而不包含细节(body)。

    3. 接口可以包含方法和/或常量,但不包含属性。接口常量与类常量具有相同的限制。接口方法是隐式抽象的。

    4. 接口不能声明构造函数或析构函数,因为这些是类的实现细节 水平。< / p >
    5. 接口中的所有方法必须具有公共可见性。

    现在让我们举个例子。 假设我们有两个玩具:一个是狗,另一个是猫 我们知道狗叫,猫叫。这两者具有相同的说话方法,但具有不同的功能或实现。 假设我们给用户一个带有说话按钮的遥控器

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

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

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