PHP中的接口有什么意义?

问题描述 投票:213回答:15

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

Abstract classes允许您执行相同的操作,同时向方法添加代码。

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

我被告知它与OO理论有关,从C ++到Java,这就是PHP的OO基础。这个概念在Java中有用但在PHP中没有用吗?它只是一种避免在抽象类中乱丢占位符的方法吗?我错过了什么吗?

php oop interface theory
15个回答
137
投票

接口的整个要点是让您可以灵活地让您的类实现多个接口,但仍然不允许多重继承。从多个类继承的问题是多种多样的,并且它上面的wikipedia页面总结得很好。

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


4
投票

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

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

至于多个抽象类,你可以有多个相互延伸的摘要(再次,我不完全确定,但我想我以前见过它)。你唯一不能扩展的是最后一堂课。


3
投票

接口不会给你的代码带来任何性能提升或类似的东西,但它们可以大大提高它的可维护性。确实可以使用抽象类(甚至非抽象类)来建立代码的接口,但是正确的接口(使用关键字定义的接口以及仅包含方法签名的接口)更容易整理并阅读。

话虽这么说,我倾向于在决定是否在类上使用接口时使用自由裁量权。有时我想要默认方法实现,或者是所有子类共有的变量。

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

然而,你的问题是关于PHP的事实使事情变得更有趣。在PHP中,键入接口仍然不是非常必要的,您可以在任何方法中提供任何内容,无论其类型如何。你可以静态地输入方法参数,但其中一些是破坏的(我相信String会导致一些打嗝)。再加上你不能输入大多数其他引用的事实,并且尝试在PHP中强制静态类型没有多大价值(此时)。正因为如此,PHP中的接口价值远远低于更强类型的语言。它们具有可读性的好处,但几乎没有。多重实现甚至不是有益的,因为您仍然需要声明方法并在实现者中为它们提供实体。


2
投票

接口就像你的基因。

抽象类就像你的父母一样。

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


1
投票

以下是PHP接口的要点

  1. 它用于在类中定义所需的no方法[如果你想加载html那么id和name是必需的,所以在这种情况下接口包括setID和setName]。
  2. 接口严格强制类包含其中定义的所有方法。
  3. 您只能在具有公共可访问性的界面中定义方法。
  4. 您还可以像类一样扩展接口。您可以使用extends关键字在php中扩展接口。
  5. 扩展多个界面。
  6. 如果两个共享函数具有相同名称,则无法实现2个接口。它会抛出错误。

示例代码:

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
投票

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

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

2.界面中的所有方法都必须在公共可见范围内。

3.Alass可以实现多个接口,而它只能从一个抽象类继承。

                                  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

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


1
投票

我不了解其他语言,接口的概念是什么。但是对于PHP,我会尽力解释它。请耐心等待,如果有帮助,请评论。

接口用作“契约”,指定一组子类的作用,但不指定它们是如何做的。

规则

  1. 接口无法实例化。
  2. 你不能在接口中实现任何方法,即。它只包含方法的.signature而不包含细节(正文)。
  3. 接口可以包含方法和/或常量,但不包含任何属性。接口常量与类常量具有相同的限制。接口方法是隐式抽象的。
  4. 接口不能声明构造函数或析构函数,因为这些是类级别的实现细节。
  5. 界面中的所有方法都必须具有公共可见性。

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

正如我们所知,狗吠和猫喵。这两种方法有相同的说法,但具有不同的功能或实施方式。假设我们给用户一个带有发言按钮的遥控器。

当用户按下发言按钮时,玩具必须说出来并不重要,无论是狗还是猫。

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

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


121
投票

这个概念在面向对象编程中很有用。对我来说,我认为接口是合同。很长一段时间,我的班级和你的班级都同意这个方法签名合同我们可以“接口”。至于抽象类,我认为更多的基类会删除一些方法,我需要填写详细信息。


63
投票

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

其中一个问题是:

“钻石问题”(有时被称为“死亡的致命钻石”)是一种歧义,当两个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);

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

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

您也可以在没有界面的情况下执行此操作,但如果使用界面,则“更安全”(因为它可以防止您出错)。该接口确保方法Create()在实现接口的所有类中具有相同的签名(相同类型和相同数量的参数)。通过这种方式,您可以确定实现IPersonService接口的任何类将具有Create()方法(在此示例中)并且仅需要1个参数($personObject)来调用/使用。

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

我希望我不会重复太多。


23
投票

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

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

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

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

这样,我说FooUser子类不是一个帐户,但是DOES就像一个可编辑的对象。同样,BarAccount从Account扩展,但不是User子类,但实现了Editable,Listable和Versionable。

将可编辑,可列表和可版本化的所有这些API添加到抽象类本身不仅会混乱和丑陋,而且会复制Account和User中的公共接口,或强制我的User对象实现Versionable,可能只是为了抛出例外。


20
投票

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

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

应用于抽象类的接口中的方法需要在该抽象类中实现。但是,将该接口应用于扩展类,该方法只需要在扩展类中实现。我可能在这里错了 - 我不尽可能经常使用接口。

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


11
投票

您将在PHP中使用接口:

  1. 隐藏实现 - 为一类对象建立访问协议,更改底层实现,而不在您使用该对象的所有位置进行重构
  2. 检查类型 - 确保参数具有特定类型$object instanceof MyInterface
  3. 在运行时强制执行参数检查
  4. 将多个行为实现到单个类中(构建复杂类型) class Car实现EngineInterface,BodyInterface,SteeringInterface {
so that a Car object ca now start(), stop() (EngineInterface) or goRight(),goLeft() (Steering interface)

和其他我现在想不到的事情

4号它可能是最难以用抽象类解决的用例。

来自Java思考:

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


9
投票

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

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

<?php

interface ImportableFeed 
{
    public function getEvents();
}

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

为了更进一步,当我回到我的日历应用程序以添加另一个提要类型时,界面证明非常有用。使用ImportableFeed接口意味着我可以通过简单地添加实现此接口的新类来继续添加更多导入不同Feed类型的类。这允许我添加大量功能,而不必为我的核心应用程序添加不必要的批量,因为我的核心应用程序只依赖于接口需要的公共方法,只要我的新feed导入类实现ImportableFeed接口然后我我知道我可以放下它并继续前进。

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

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

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

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


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

在许多情况下,提供基类(抽象与否)是没有意义的,因为实现变化很大,并且除了少数方法之外不共享任何共同点。

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


5
投票

在我看来,接口应该优于非功能抽象类。我不会感到惊讶,因为那里只有一个性能上升,因为只有一个对象实例化,而不是解析两个,组合它们(虽然,我不能确定,我不熟悉内部工作OOP PHP)。

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

tl; dr:interfaces定义了需要遵循的方法列表(思考API),而抽象类提供了一些基本/通用功能,子类可以根据特定需求进行优化。

© www.soinside.com 2019 - 2024. All rights reserved.