PHP 中的接口有什么意义?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20463/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 21:12:00  来源:igfitidea点击:

What is the point of interfaces in PHP?

phpoopinterfacetheory

提问by mk.

Interfacesallow you to create code which defines the methods of classes that implement it. You cannot however add any code to those methods.

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

Abstract classesallow you to do the same thing, along with adding code to the method.

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

Now if you can achieve the same goal with abstract classes, why do we even need the concept of interfaces?

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

I've been told that it has to do with OO theory from C++ to Java, which is what PHP's OO stuff is based on. Is the concept useful in Java but not in PHP? Is it just a way to keep from having placeholders littered in the abstract class? Am I missing something?

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

采纳答案by Craig H

The entire point of interfaces is to give you the flexibility to have your class be forced to implement multiple interfaces, but still not allow multiple inheritance. The issues with inheriting from multiple classes are many and varied and the wikipediapage on it sums them up pretty well.

接口的全部意义在于让您可以灵活地强制您的类实现多个接口,但仍然不允许多重继承。从多个类继承的问题多种多样,维基百科页面对其进行了很好的总结。

Interfaces are a compromise. Most of the problems with multiple inheritance don't apply to abstract base classes, so most modern languages these days disable multiple inheritance yet call abstract base classes interfaces and allows a class to "implement" as many of those as they want.

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

回答by John Downey

The concept is useful all around in object oriented programming. To me I think of an interface as a contract. So long my class and your class agree on this method signature contract we can "interface". As for abstract classes those I see as more of base classes that stub out some methods and I need to fill in the details.

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

回答by Jo Smo

Why would you need an interface, if there are already abstract classes?To prevent multiple inheritance (can cause multiple known problems).

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

One of such problems:

此类问题之一:

The "diamond problem" (sometimes referred to as the "deadly diamond of death") is an ambiguity that arises when two classes B and C inherit from A and class D inherits from both B and C. If there is a method in A that B and C have overridden, and D does not override it, then which version of the method does D inherit: that of B, or that of C?

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

Source: https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem

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

Why/When to use an interface?An example... All cars in the world have the same interface (methods)... AccelerationPedalIsOnTheRight(), BrakePedalISOnTheLeft(). Imagine that each car brand would have these "methods" different from another brand. BMW would have The brakes on the right side, and Honda would have brakes on the left side of the wheel. People would have to learn how these "methods" work every time they would buy a different brand of car. That's why it's a good idea to have the same interface in multiple "places."

为什么/何时使用接口?一个例子......世界上所有的汽车都有相同的界面(方法)...... AccelerationPedalIsOnTheRight(), BrakePedalISOnTheLeft(). 想象一下,每个汽车品牌都会有这些与另一个品牌不同的“方法”。宝马将在右侧刹车,而本田将在车轮左侧刹车。人们每次购买不同品牌的汽车时都必须学习这些“方法”的工作原理。这就是为什么在多个“位置”中使用相同的界面是个好主意的原因。

What does an interface do for you (why would someone even use one)? An interface prevents you from making "mistakes" (it assures you that all classes which implement a specific interface, will all have the methods which are in the interface).

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

// 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).
    }
}

This way, the Create()method will always be used the same way. It doesn't matter if we are using the MySqlPersonclass or the MongoPersonclass. The way how we are using a method stays the same (the interface stays the same).

这样,该Create()方法将始终以相同的方式使用。我们使用的是MySqlPerson类还是MongoPerson类都没有关系。我们使用方法的方式保持不变(接口保持不变)。

For example, it will be used like this (everywhere in our code):

例如,它将像这样使用(在我们的代码中的任何地方):

new MySqlPerson()->Create($personObject);
new MongoPerson()->Create($personObject);

This way, something like this can't happen:

这样,这样的事情就不会发生:

new MySqlPerson()->Create($personObject)
new MongoPerson()->Create($personsName, $personsAge);

It's much easier to remember one interface and use the same one everywhere, than multiple different ones.

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

This way, the inside of the Create()method can be different for different classes, without affecting the "outside" code, which calls this method. All the outside code has to know is that the method Create()has 1 parameter ($personObject), because that's how the outside code will use/call the method. The outside code doesn't care what's happening inside the method; it only has to know how to use/call it.

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

You can do this without an interface as well, but if you use an interface, it's "safer" (because it prevents you to make mistakes). The interface assures you that the method Create()will have the same signature (same types and a same number of parameters) in all classes that implement the interface. This way you can be sure that ANY class which implements the IPersonServiceinterface, will have the method Create()(in this example) and will need only 1 parameter ($personObject) to get called/used.

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

A class that implements an interface must implement all methods, which the interface does/has.

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

I hope that I didn't repeat myself too much.

我希望我没有重复太多。

回答by Sam McAfee

The difference between using an interface and an abstract class has more to do with code organization for me, than enforcement by the language itself. I use them a lot when preparing code for other developers to work with so that they stay within the intended design patterns. Interfaces are a kind of "design by contract" whereby your code is agreeing to respond to a prescribed set of API calls that may be coming from code you do not have aceess to.

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

While inheritance from abstract class is a "is a" relation, that isn't always what you want, and implementing an interface is more of a "acts like a" relation. This difference can be quite significant in certain contexts.

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

For example, let us say you have an abstract class Account from which many other classes extend (types of accounts and so forth). It has a particular set of methods that are only applicable to that type group. However, some of these account subclasses implement Versionable, or Listable, or Editable so that they can be thrown into controllers that expect to use those APIs. The controller does not care what type of object it is

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

By contrast, I can also create an object that does not extend from Account, say a User abstract class, and still implement Listable and Editable, but not Versionable, which doesn't make sense here.

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

In this way, I am saying that FooUser subclass is NOT an account, but DOES act like an Editable object. Likewise BarAccount extends from Account, but is not a User subclass, but implements Editable, Listable and also Versionable.

通过这种方式,我是说 FooUser 子类不是一个帐户,但确实像一个 Editable 对象。同样,BarAccount 从 Account 扩展,但不是 User 子类,但实现了 Editable、Listable 和 Versionable。

Adding all of these APIs for Editable, Listable and Versionable into the abstract classes itself would not only be cluttered and ugly, but would either duplicate the common interfaces in Account and User, or force my User object to implement Versionable, probably just to throw an exception.

将所有这些用于 Editable、Listable 和 Versionable 的 API 添加到抽象类本身中不仅会变得混乱和丑陋,而且会复制 Account 和 User 中的通用接口,或者强制我的 User 对象实现 Versionable,可能只是为了抛出一个例外。

回答by Ross

Interfaces are essentially a blueprint for what you can create. They define what methods a class must have, but you can create extra methods outside of those limitations.

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

I'm not sure what you mean by not being able to add code to methods - because you can. Are you applying the interface to an abstract class or the class that extends it?

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

A method in the interface applied to the abstract class will need to be implemented in that abstract class. However apply that interface to the extending class and the method only needs implementing in the extending class. I could be wrong here - I don't use interfaces as often as I could/should.

应用于抽象类的接口中的方法需要在该抽象类中实现。然而,将该接口应用于扩展类,并且该方法只需要在扩展类中实现。我在这里可能是错的 - 我不会像我可以/应该那样经常使用接口。

I've always thought of interfaces as a pattern for external developers or an extra ruleset to ensure things are correct.

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

回答by catalin.costache

You will use interfaces in PHP:

您将在 PHP 中使用接口:

  1. To hide implementation - establish an access protocol to a class of objects an change the underlying implementation without refactoring in all the places you've used that objects
  2. To check type - as in making sure that a parameter has a specific type $object instanceof MyInterface
  3. To enforce parameter checking at runtime
  4. To implement multiple behaviours into a single class (build complex types)

    class Car implements EngineInterface, BodyInterface, SteeringInterface {

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

    类 Car 实现 EngineInterface、BodyInterface、SteeringInterface {

这样一个CarCar对象现在可以start()start()stop()stop()(EngineInterface)或goRight()goRight()goLeft()goLeft()(转向接口)

and other things I cannot think of right now

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

Number 4 it's probably the most obvious use case that you cannot address with abstract classes.

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

From Thinking in Java:

从 Java 中思考:

An interface says, “This is what all classes that implement this particular interface will look like.” Thus, any code that uses a particular interface knows what methods can be called for that interface, and that's all. So the interface is used to establish a “protocol” between classes.

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

回答by Houghtelin

Interfaces exist not as a base on which classes can extend but as a map of required functions.

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

The following is an example of using an interface where an abstract class does not fit:
Lets say I have a calendar application that allows users to import calendar data from external sources. I would write classes to handle importing each type of data source (ical, rss, atom, json) Each of those classes would implement a common interface that would ensure they all have the common public methods that my application needs to get the data.

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

<?php

interface ImportableFeed 
{
    public function getEvents();
}

Then when a user adds a new feed I can identify the type of feed it is and use the class developed for that type to import the data. Each class written to import data for a specific feed would have completely different code, there may otherwise be very few similarities between the classes outside of the fact that they are required to implement the interface that allows my application to consume them. If I were to use an abstract class, I could very easily ignore the fact that I have not overridden the getEvents() method which would then break my application in this instance whereas using an interface would not let my app run if ANY of the methods defined in the interface do not exist in the class that implemented it. My app doesn't have to care what class it uses to get data from a feed, only that the methods it needs to get that data are present.

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

To take this a step further, the interface proves to be extremely useful when I come back to my calendar app with the intent of adding another feed type. Using the ImportableFeed interface means I can continue adding more classes that import different feed types by simply adding new classes that implement this interface. This allows me to add tons of functionality without having to add unnecessarily bulk to my core application since my core application only relies on there being the public methods available that the interface requires so as long as my new feed import classes implement the ImportableFeed interface then I know I can just drop it in place and keep moving.

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

This is just a very simple start. I can then create another interface that all my calendar classes can be required to implement that offers more functionality specific to the feed type the class handles. Another good example would be a method to verify the feed type, etc.

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

This goes beyond the question but since I used the example above: Interfaces come with their own set of issues if used in this manner. I find myself needing to ensure the output that is returned from the methods implemented to match the interface and to achieve this I use an IDE that reads PHPDoc blocks and add the return type as a type hint in a PHPDoc block of the interface which will then translate to the concrete class that implements it. My classes that consume the data output from the classes that implement this interface will then at the very least know it's expecting an array returned in this example:

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

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

There isn't much room in which to compare abstract classes and interfaces. Interfaces are simply maps that when implemented require the class to have a set of public interfaces.

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

回答by Outlaw Programmer

Interfaces aren't just for making sure developers implement certain methods. The idea is that because these classes are guaranteed to have certain methods, you can use these methods even if you don't know the class's actual type. Example:

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

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

In many cases, it doesn't make sense to provide a base class, abstract or not, because the implementations vary wildly and don't share anything in common besides a few methods.

在许多情况下,提供基类,无论是否抽象都是没有意义的,因为实现变化很大,除了一些方法之外没有任何共同点。

Dynamically typed languages have the notion of "duck-typing" where you don't need interfaces; you are free to assume that the object has the method that you're calling on it. This works around the problem in statically typed languages where your object has some method (in my example, read()), but doesn't implement the interface.

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

回答by Henrik Paul

In my opinion, interfaces should be preferred over non-functional abstract classes. I wouldn't be surprised if there would be even a performance hit there, as there is only one object instantiated, instead of parsing two, combining them (although, I can't be sure, I'm not familiar with the inner workings of OOP PHP).

在我看来,接口应该比非功能性抽象类更受欢迎。如果那里甚至会出现性能问题,我不会感到惊讶,因为只有一个对象实例化,而不是解析两个,将它们组合起来(虽然,我不能确定,我不熟悉内部工作原理OOP PHP)。

It is true that interfaces are less useful/meaningful than compared to, say, Java. On the other hand, PHP6 will introduce even more type hinting, including type hinting for return values. This should add some value to PHP interfaces.

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

tl;dr: interfaces defines a list of methods that need to be followed (think API), while an abstract class gives some basic/common functionality, which the subclasses refine to specific needs.

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

回答by Ross

I can't remember if PHP is different in this respect, but in Java, you can implement multiple Interfaces, but you can't inherit multiple abstract classes. I'd assume PHP works the same way.

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

In PHP you can apply multiple interfaces by seperating them with a comma (I think, I don't find that a clean soloution).

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

As for multiple abstract classes you could have multiple abstracts extending each other (again, I'm not totally sure about that but I think I've seen that somewhere before). The only thing you can't extend is a final class.

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