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

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

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

有人告诉我,这与从 C++ 到 Java 的 OO 理论有关,这也是 PHP 的 OO 内容的基础。这个概念在 Java 中有用但在 PHP 中无效吗?这是否只是避免在抽象类中出现占位符的一种方法?我错过了什么吗?

有帮助吗?

解决方案

接口的全部意义在于为您提供灵活性,让您的类被迫实现多个接口,但仍然不允许多重继承。从多个类继承的问题多种多样,并且 维基百科 其页面很好地总结了它们。

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

其他提示

这个概念在面向对象编程中很有用。对我来说,我将界面视为契约。只要我的类和你的类就这个方法签名契约达成一致,我们就可以“接口”。至于抽象类,我认为更多的是基类,它们消除了一些方法,我需要填写详细信息。

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

此类问题之一:

“钻石问题”(有时被称为“死亡的致命钻石”)是一种歧义,当两个类B和C从A和D类继承的B和C级继承时,就会产生。如果有一种方法是B和C覆盖的方法,并且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) 被调用/使用。

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

我希望我没有重复太多。

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

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

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

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

通过这种方式,我说 FooUser 子类不是一个帐户,但它的行为就像一个可编辑对象。同样,BarAccount 继承自 Account,但不是 User 子类,但实现了 Editable、Listable 和 Versionable。

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

界面本质上是您可以创建的内容的蓝图。他们定义了类的方法 一定有, ,但您可以在这些限制之外创建额外的方法。

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

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

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

您将使用 PHP 中的接口:

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

    汽车类实现了EngineInterface、BodyInterface、SteeringInterface {

这样一个 Car 现在对象 start(), stop() (引擎接口)或 goRight(),goLeft() (转向接口)

以及其他我现在想不到的事情

第四,它可能是无法用抽象类解决的最明显的用例。

来自 Java 思维:

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

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

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

<?php

interface ImportableFeed 
{
    public function getEvents();
}

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

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

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

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

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

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

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

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

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

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

长话短说:接口定义了需要遵循的方法列表(想想 API),而抽象类提供了一些基本/通用的功能,子类可以根据特定的需求进行细化。

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

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

至于多个抽象类,您可以有多个相互扩展的抽象(同样,我对此并不完全确定,但我想我以前在某处见过)。唯一不能延长的就是最后一堂课。

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

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

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

不过,事实上你的问题是关于 PHP 的,这让事情变得更有趣了。在 PHP 中,向接口键入内容仍然不是非常必要,您几乎可以将任何内容提供给任何方法,无论其类型如何。您可以静态键入方法参数,但其中一些参数已损坏(我相信字符串会导致一些问题)。再加上您无法键入大多数其他引用的事实,并且尝试在 PHP 中强制静态键入没有太大价值(在此刻)。正因为如此,接口的价值 在 PHP 中, 在此刻 远小于强类型语言中的情况。它们的优点是可读性强,但除此之外就没什么好处了。多重实现甚至没有什么好处,因为您仍然必须声明方法并在实现者中为它们提供主体。

接口就像你的基因。

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

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

以下是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);

我们看到抽象类和接口的相似之处在于它们都提供必须在子类中实现的抽象方法。然而,它们仍然存在以下差异:

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

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

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

接口充当“契约”,指定一组子类做什么,但不指定它们如何做。

规则

  1. 接口无法实例化。

  2. 您不能在接口中实现任何方法,即它只包含方法的签名,但不包含详细信息(正文)。

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

  4. 接口不得声明构造函数或破坏者,因为这些是班级级别的实现细节。
  5. 接口中的所有方法都必须具有公共可见性。

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

正如我们所知,狗会叫,猫会叫。这两者具有相同的说话方法,但具有不同的功能或实现。假设我们为用户提供一个带有讲话按钮的遥控器。

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

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top