PHP接口的意义是什么?


224

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

抽象类使您可以做同样的事情,并向方法中添加代码。

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

有人告诉我,它与从C ++到Java的OO理论有关,这是PHP的OO东西所基于的。这个概念在Java中有用但在PHP中没有用吗?这只是一种避免在抽象类中乱扔占位符的方法吗?我想念什么吗?


4
您必须阅读以下内容:stackoverflow.com/a/384067/14673
Luc M

可以肯定这是精神上的帮助和沟通上的帮助。接口是api的出色教学工具,因为它们将api公开的服务以抽象的方式捆绑在一起,无需了解实现即可读取。这很昂贵,但是由于对接口熟悉的人可以在不需要类的情况下直接使用函数,从而节省了指针。没有语言定义的接口,对于许多程序员来说,提前计划可能会很棘手,因为习惯于“ oop语言”的人们通常更喜欢使用接口设计,而不是在纸上进行设计。
德米特里(Dmitry)

Answers:


142

接口的全部目的是使您能够灵活地使您的类被强制实现多个接口,但仍不允许多重继承。从多个类继承的问题很多,而且各种各样,其上的Wikipedia页面对其进行了很好的总结。

接口是一种折衷。多重继承的大多数问题都不适用于抽象基类,因此,当今大多数现代语言都禁用了多重继承,但调用了抽象基类接口,并允许一个类“实现”所需的任意多个。


39
也可以说*接口为零实现的类提供了设计。*抽象类提供了一些设计和实现。当子类共享一些实现上的相似之处,但在某些实现上有所不同时,抽象类最有用。
Jrgns 2012年

@Craig,多重继承没有内在的问题,只是当前的语言还不足以正确实现它们。实际上,可以很容易地解决“问题”,例如,为同名继承函数声明显式继承路径可以解决钻石难题。
Pacerier,2015年

15
这根本不是接口。这不是对多重继承的妥协,而是关于为要实现的对象以及要使用的其他对象/方法创建概念性和抽象性契约。接口是用于多态而不是直接继承的工具。
马达拉的幽灵

123

该概念在面向对象编程中非常有用。对我来说,我认为接口是一种合同。只要我的班级和您的班级就此方法签名合同达成协议,我们就可以“接口”。至于抽象类,我认为它们更多是一些某些方法的基类,因此我需要填写细节。


这让我有点理解。就像命名空间一样-共享代码更易于使用且没有冲突。两个人在同一基础上上课会更容易,对吗?
RedClover

我们是否不需要像PHP这样的语言将“接口”的一般概念与具体接口区分开?例如,任何函数都有一个“接口”,该接口定义您如何使用它并隐藏其实现。因此,这种“合同”界面不需要特殊的语言功能。因此,语言功能必须用于其他功能(或其他功能)
。– UuDdLrLrSs

70

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

这些问题之一:

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

资料来源:https : //zh.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接口的ANY类具有该方法Create()(在此示例中),并且仅需要1个参数($personObject)即可被调用/使用。

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

我希望我不要重复太多。


与汽车踏板真的很相似!
詹姆斯

24

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

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

例如,假设您有一个抽象类Account,从中扩展了许多其他类(帐户类型等)。它具有一组仅适用于该类型组的特定方法。但是,这些帐户子类中的某些实现了Versionable,Listable或Editable,以便可以将它们扔入希望使用这些API的控制器中。控制器不在乎它是什么类型的对象

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

这样,我说的是FooUser子类不是一个帐户,但是它的行为就像一个Editable对象。同样,BarAccount从Account扩展而来,但不是User子类,而是实现Editable,Listable和Versionable。

将所有这些可编辑,可列出和可版本控制的API添加到抽象类本身中,不仅会变得凌乱和丑陋,而且会复制Account和User中的通用接口,或者强迫我的User对象实现Versionable,可能只是抛出一个例外。


就在这里。严格要求开发人员在不扩展或覆盖您的方法的情况下使用您的方法
Nick

21

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

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

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

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


9
在PHP中,接口仅包含方法声明,而不包含实际实现。但是,抽象类允许您向扩展它的类所继承的方法“添加代码”。我相信这就是mk所指的区别。
nocash

13

您将在PHP中使用接口:

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

    Car类实现EngineInterface,BodyInterface,SteeringInterface {

这样一个Car对象现在可以start()stop()(EngineInterface)或goRight()goLeft()(Steering interface)

和其他我现在无法想到的事情

第4个可能是您无法使用抽象类解决的最明显的用例。

从Java思考:

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


10

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

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

<?php

interface ImportableFeed 
{
    public function getEvents();
}

然后,当用户添加新的提要时,我可以确定提要的类型,并使用针对该类型开发的类来导入数据。为为特定的提要导入数据而编写的每个类都将具有完全不同的代码,否则这些类之间可能只有很少的相似性,而事实是它们是实现应用程序允许使用它们的接口所必需的。如果我要使用抽象类,则可以很容易地忽略一个事实,即我没有重写getEvents()方法,该方法将在这种情况下破坏我的应用程序,而如果使用任何方法,使用接口都不会让我的应用程序运行接口中定义的内容在实现它的类中不存在。我的应用程序不必关心它用于从Feed中获取数据的类,

为了更进一步,当我返回日历应用程序以添加其他供稿类型时,该界面被证明非常有用。使用ImportableFeed接口意味着我可以继续添加更多的类,只需添加实现此接口的新类,即可导入不同的提要类型。这使我可以添加大量功能,而不必向核心应用程序添加不必要的批量,因为核心应用程序仅依赖于接口需要的可用公共方法,只要我的新Feed导入类实现ImportableFeed接口,我知道我可以将其放到适当的位置并继续前进。

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

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

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

比较抽象类和接口的空间不大。接口只是简单的映射,实现时要求类具有一组公共接口。


好解释:)谢谢!
Razvan.432

仅添加信息:在抽象类中,如果将方法声明为抽象,则其行为类似于接口,因此您不能忽略未覆盖getEvents()的事实。该应用程序将以与界面相同的方式失败。
Rikudou_Sennin

8

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

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


6

我认为,接口应该比非功能性抽象类更可取。如果仅实例化一个对象,我将不会感到惊讶,因为仅实例化了一个对象,而不是解析两个对象,将它们组合在一起(尽管我不确定,我对内部工作原理并不熟悉。 OOP PHP)。

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

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


PHP 6将永远不会发布。PHP 6是一个在2005年至2010年间正在开发的项目,但它被延迟并最终被取消。下一个版本是PHP 7,主要是为了避免与以前的PHP 6项目混淆。
阿舒贾(Ashu Jha)2016年

5

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

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

至于多个抽象类,则可以有多个抽象相互扩展(再次,我对此不太确定,但我认为我之前已经看到过)。您唯一不能扩展的是最后一堂课。


4

接口不会给您的代码带来任何性能上的提升或类似的提升,但是它们在使代码可维护方面可以走很长一段路。确实可以使用抽象类(甚至是非抽象类)来建立代码的接口,但是正确的接口(使用关键字定义的接口并且仅包含方法签名)很容易实现。整理并阅读。

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

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

但是,您的问题是关于PHP的事实,这使事情变得更加有趣。在PHP中,对接口的键入仍然不是十分必要,在这里,您几乎可以将任何内容馈送给任何方法,而不管其类型如何。您可以静态地键入方法参数,但是其中一些参数是坏的(我相信字符串会引起一些麻烦)。再加上您不能键入大多数其他引用这一事实,并且试图在PHP中强制进行静态键入(在这一点上)没有太大价值。因此,在这一点上,PHP中接口的价值远远不及更强类型的语言。它们具有可读性的优点,但几乎没有其他优点。多重实现甚至没有好处,因为您仍然必须声明方法并将它们的主体提供给实现者。


2

以下是PHP接口的要点

  1. 它用于定义类中不需要的方法[如果要加载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);

2

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

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

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


2

界面就像您的基因。

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

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


2

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

接口充当“合同”,指定一组子类的功能,但不指定其操作方式。

规则

  1. 接口不能实例化。

  2. 您不能在接口中实现任何方法,即它仅包含该方法的.signature,而不包含details(body)。

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

  4. 接口不得声明构造函数或析构函数,因为它们是类级别的实现细节。

  5. 界面中的所有方法必须具有公共可见性。

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

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

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

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.