接口还是抽象类:使用哪一个?
请解释一下我什么时候应该使用 PHP 接口
以及什么时候应该使用抽象类
?
如何将我的抽象类
更改为接口
?
Please explain when I should use a PHP interface
and when I should use an abstract class
?
How I can change my abstract class
in to an interface
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
当您想要强制在您的系统中工作的开发人员(包括您自己)在他们将要构建的类上实现一定数量的方法时,请使用接口。
当您想要强制在您的系统中工作的开发人员(包括您自己)实现一定数量的方法并且您想要提供一些基本方法来帮助他们开发其子类时,请使用抽象类。
另一件需要记住的事情是客户端类只能扩展一个抽象类,而它们可以实现多个接口。因此,如果您在抽象类中定义行为契约,则意味着每个子类可能只符合一个契约。有时,当您想迫使用户程序员沿着特定的道路前进时,这是一件好事。其他时候情况会很糟糕。想象一下,如果 PHP 的 Countable 和 Iterator 接口是抽象类而不是接口。
当您不确定该走哪条路时,常见的一种方法(如 cletus 下面)是创建一个接口,然后让你的抽象类实现该接口。
Use an interface when you want to force developers working in your system (yourself included) to implement a set number of methods on the classes they'll be building.
Use an abstract class when you want to force developers working in your system (yourself included) to implement a set numbers of methods and you want to provide some base methods that will help them develop their child classes.
Another thing to keep in mind is client classes can only extend one abstract class, whereas they can implement multiple interfaces. So, if you're defining your behavior contracts in abstract classes, that means each child class may only conform to a single contract. Sometimes this a good thing, when you want to force your user-programmers along a particular path. Other times it would be bad. Imagine if PHP's Countable and Iterator interfaces were abstract classes instead of interfaces.
One approach that's common when you're uncertain which way to go (as mentioned by cletus below) is to create an interface, and then have your abstract class implement that interface.
抽象类
和接口
之间的区别:抽象类
抽象类可以提供一些功能并且 >将其余部分留给派生类。
派生类可以覆盖也可以不覆盖基类中定义的具体函数。
从抽象类扩展的子类在逻辑上应该是相关的。
界面
界面不能包含任何功能。它仅包含方法的声明。
派生类必须为接口中定义的所有方法提供代码。
完全不同且不相关的类可以使用接口在逻辑上分组在一起。
The differences between an
Abstract Class
and anInterface
:Abstract Classes
An abstract class can provide some functionality and leave the rest for derived class.
The derived class may or may not override the concrete functions defined in the base class.
A child class extended from an abstract class should logically be related.
Interface
An interface cannot contain any functionality. It only contains declaration of the methods.
The derived class MUST provide code for all the methods defined in the interface.
Completely different and non-related classes can be logically grouped together using an interface.
为什么要使用抽象类?以下是 https://www.php 的简单示例.net/manual/en/language.oop5.abstract.php#95404,作者:用户 mbajoras。
抽象类可以有空方法和工作/具体方法。在接口中,定义的函数不能有主体。在抽象类中,他们可以。
一个现实世界的例子:
Why to use abstract classes? The following is a simple example from https://www.php.net/manual/en/language.oop5.abstract.php#95404 by user mbajoras.
Abstract classes can have both empty and working/concrete methods. In interfaces, functions defined there cannot have a body. In abstract classes, they can.
A real world example:
最佳实践是使用接口来指定契约并使用抽象类作为其一种实现。该抽象类可以填充大量样板文件,因此您可以通过覆盖您需要或想要的内容来创建实现,而无需强迫您使用特定的实现。
Best practice is to use an interface to specify the contract and an abstract class as just one implementation thereof. That abstract class can fill in a lot of the boilerplate so you can create an implementation by just overriding what you need to or want to without forcing you to use a particular implementation.
只是将其混入其中,但正如 Cletus 提到的将接口与抽象类结合使用,我经常使用接口来阐明我的设计思维。
例如:
这样,任何阅读我的代码的人(以及知道装饰器模式是什么的人)都会立即知道 a) 我如何构建解析器,b) 能够看到使用哪些方法来实现装饰器模式。
另外,我可能不是 Java/C++/etc 程序员,但数据类型可以在这里发挥作用。您的对象属于一种类型,当您以编程方式传递它们时,该类型很重要。将可合同项移动到接口中仅指示方法返回的类型,而不指示实现它的类的基类型。
已经晚了,我想不出更好的伪代码示例,但这里是:
Just to throw this into the mix, but as Cletus mentioned using an interface in conjunction with an abstract class, I often use the interface to clarify my design thinking.
For instance:
That way, anyone reading my code (and who knows what a Decorator Pattern is) will know right away a) how I build my parser and b) be able to see what methods are used to implement the decorator pattern.
Also, and I may be off base here not being a Java/C++/etc programmer, but data types can come into play here. Your objects are of a type, and when you pass them around the type matters programmatically. Moving your contractable items into the interface only dictates the types that the methods return, but not the base type of the class that implements it.
It's late and I can't think of a better psudo-code example, but here goes:
另外,我想在这里补充一点,任何其他 OO 语言也具有某种接口和抽象并不意味着它们具有与 PHP 中相同的含义和目的。抽象/接口的使用略有不同,而 PHP 中的接口实际上没有真正的功能。它们仅用于语义和方案相关的原因。关键是让一个项目尽可能灵活、可扩展并且对未来的扩展安全,无论开发人员以后是否有完全不同的使用计划。
如果您的母语不是英语,您可能会查找抽象和接口实际上是什么。并寻找同义词。
作为一个比喻,这可能会对您有所帮助:
界面
比方说,您用草莓烘焙了一种新的蛋糕,并且制作了一个描述成分和步骤的食谱。
只有您知道为什么它味道这么好并且您的客人喜欢它。
然后您决定发布您的食谱,以便其他人也可以尝试该蛋糕。
重点是
这正是描述接口的内容。它是一个指南,一组遵守食谱内容的说明。
就像您在 PHP 中创建一个项目一样,您希望在 GitHub 上或与您的朋友或其他任何地方提供代码。
界面是人们可以做什么和你不应该做什么。遵守它的规则——如果你违反其中一条,整个结构就会被破坏。
抽象
继续这个比喻...想象一下,你是这次吃蛋糕的客人。然后你现在就用食谱来尝试那个蛋糕。
但您想添加新成分或更改/跳过食谱中描述的步骤。那么接下来会发生什么呢?计划该蛋糕的不同版本。
这次是黑浆果,不是草莓,还有更多香草奶油……好吃。
您可以将其视为原始蛋糕的延伸。你基本上通过创建一个新的配方来对其进行抽象,因为它有一点不同。它有一些新步骤和其他成分。然而,黑莓版本有一些你从原版中继承的部分——这些是每种蛋糕都必须具备的基本步骤。就像牛奶一样的成分 - 这是每个派生类都拥有的。
现在您想要交换成分和步骤,这些必须在该蛋糕的新版本中定义。这些是必须为新蛋糕定义的抽象方法,因为蛋糕中应该有一个水果,但是是哪个呢?所以这次你吃黑浆果。完毕。
到这里,您已经扩展了蛋糕,遵循界面并从中抽象出步骤和成分。
Also, just would like to add here that just because any other OO language has some kind of interfaces and abstraction too doesn't mean they have the same meaning and purpose as in PHP. The use of abstraction/interfaces is slightly different while interfaces in PHP actually don't have a real function. They merely are used for semantic and scheme-related reasons. The point is to have a project as much flexible as possible, expandable and safe for future extensions regardless whether the developer later on has a totally different plan of use or not.
If your English is not native you might lookup what Abstraction and Interfaces actually are. And look for synonyms too.
And this might help you as a metaphor:
INTERFACE
Let's say, you bake a new sort of cake with strawberries and you made up a recipe describing the ingredients and steps.
Only you know why it's tasting so well and your guests like it.
Then you decide to publish your recipe so other people can try that cake as well.
The point here is
Exactly THIS is what describes interfaces. It is a guide, a set of instructions which observe the content of the recipe.
Same as if you would create a project in PHP and you want to provide the code on GitHub or with your mates or whatever.
An interface is what people can do and what you should not. Rules that hold it - if you disobey one, the entire construct will be broken.
ABSTRACTION
To continue with this metaphor here... imagine, you are the guest this time eating that cake. Then you are trying that cake using the recipe now.
But you want to add new ingredients or change/skip the steps described in the recipe. So what comes next? Plan a different version of that cake.
This time with black berries and not straw berries and more vanilla cream...yummy.
This is what you could consider an extension of the original cake. You basically do an abstraction of it by creating a new recipe because it's a lil different. It has a few new steps and other ingredients. However, the black berry version has some parts you took over from the original - these are the base steps that every kind of that cake must have. Like ingredients just as milk - That is what every derived class has.
Now you want to exchange ingredients and steps and these MUST be defined in the new version of that cake. These are abstract methods which have to be defined for the new cake, because there should be a fruit in the cake but which? So you take the black berries this time. Done.
There you go, you have extended the cake, followed the interface and abstracted steps and ingredients from it.
主要区别是抽象类可以包含默认实现,而接口则不能。
接口是没有任何实现的行为契约。
The main difference is an abstract class can contain default implementation whereas an interface cannot.
An interface is a contract of behaviour without any implementation.
添加到一些已经很好的答案:
抽象类让您提供某种程度的实现,接口是纯模板。接口只能定义功能,而永远不能实现它。
任何实现该接口的类都承诺实现它定义的所有方法,或者必须将其声明为抽象。
任何实现该
接口可以帮助管理这样一个事实:与 Java 一样,PHP 不支持多重继承。 PHP 类只能扩展单个父类。但是,您可以让一个类承诺实现任意数量的接口。
类型:对于它实现的每个接口,该类采用相应的类型。因为任何类都可以实现一个接口(或多个接口),
接口有效地连接了不相关的类型。
一个类可以扩展超类并实现任意数量的接口:
当您只需要提供一个没有任何实现的模板,并且您希望确保实现该接口的任何类将具有与实现该接口的任何其他类(至少)相同的方法时,请使用接口。
当您想要为其他对象(部分构建的类)创建基础时,请使用抽象类。扩展抽象类的类将使用定义/实现的一些属性或方法:
这是一个简化的案例/示例。删除所有实施细节。例如,将抽象类从: 更改
为:
To add to some of the already excellent answers:
Abstract classes let you provide some degree of implementation, interfaces are pure templates. An interface can only define functionality, it can never implement it.
Any class that implements the interface commits to implementing all the methods it defines or it must be declared abstract.
Interfaces can help to manage the fact that, like Java, PHP does not support multiple inheritance. A PHP class can only extend a single parent. However, you can make a class promise to implement as many interfaces as you want.
type: for each interface it implements, the class takes on the corresponding type. Because any class can implement an interface (or more interfaces),
interfaces effectively join types that are otherwise unrelated.
a class can both extend a superclass and implement any number of interfaces:
Use an interface when you need to provide only a template with no implementation what so ever, and you want to make sure any class that implements that interface will have the same methods as any other class that implements it (at least).
Use an abstract class when you want to create a foundation for other objects (a partially built class). The class that extends your abstract class will use some properties or methods defined/implemented:
Here is a simplified case/example. Take out any implementation details out. For example, change your abstract class from:
to:
从哲学的角度来看:
抽象类代表“是”关系。假设我有水果,那么我会有一个 Fruit 抽象类,它共享共同的责任和共同的行为。
接口代表“应该做”的关系。在我看来(这是初级开发人员的意见),界面应该由一个动作或接近动作的东西来命名,(抱歉,找不到这个词,我不是英语母语者)让我们说IEatable。你知道它可以吃,但你不知道你吃的是什么。
从编码的角度来看:
如果您的对象有重复的代码,则表明它们具有共同的行为,这意味着您可能需要一个抽象类来重用代码,而使用接口则无法做到这一点。
另一个区别是,一个对象可以根据需要实现任意多个接口,但由于“钻石问题”,您只能有一个抽象类(查看此处了解原因! http://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem)
我可能忘记了一些要点,但我希望它能澄清一些事情。
PS:“是”/“应该做”是由 Vivek Vermani 的答案带来的,我并不是有意窃取他的答案,只是为了重复使用这些术语,因为我喜欢它们!
From a phylosophic point of view :
An abstract class represents an "is a" relationship. Lets say I have fruits, well I would have a Fruit abstract class that shares common responsabilities and common behavior.
An interface represents a "should do" relationship. An interface, in my opinion (which is the opinion of a junior dev), should be named by an action, or something close to an action, (Sorry, can't find the word, I'm not an english native speaker) lets say IEatable. You know it can be eaten, but you don't know what you eat.
From a coding point of view :
If your objects have duplicated code, it is an indication that they have common behavior, which means you might need an abstract class to reuse the code, which you cannot do with an interface.
Another difference is that an object can implement as many interfaces as you need, but you can only have one abstract class because of the "diamond problem" (check out here to know why! http://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem)
I probably forget some points, but I hope it can clarify things.
PS : The "is a"/"should do" is brought by Vivek Vermani's answer, I didn't mean to steal his answer, just to reuse the terms because I liked them!
抽象类和接口之间的技术差异已经在其他答案中准确列出。为了面向对象编程,我想在编写代码时添加一个解释,以在类和接口之间进行选择。
类应该代表实体,而接口应该代表行为。
让我们举个例子。计算机显示器是一个实体,应该表示为一个类。
它旨在为您提供一个显示界面,因此功能应该由界面来定义。
正如其他答案中所解释的那样,还有很多其他事情需要考虑,但这是大多数人在编码时忽略的最基本的事情。
The technical differences between an abstract class and an interface are already listed in the other answers precisely. I want to add an explanation to choose between a class and an interface while writing the code for the sake of object oriented programming.
A class should represent an entity whereas an interface should represent the behavior.
Let's take an example. A computer monitor is an entity and should be represented as a class.
It is designed to provide a display interface to you, so the functionality should be defined by an interface.
There are many other things to consider as explained in the other answers, but this is the most basic thing which most of the people ignore while coding.
只是想添加一个示例,说明您何时可能需要同时使用两者。我目前正在通用 ERP 解决方案中编写一个绑定到数据库模型的文件处理程序。
这样,我就可以拥有针对不同文件的多个模板和一组具有明显区别的通用接口方法。该接口为访问方法提供了正确的类比,而不是基抽象类的类比。
进一步,当我将为不同的文件存储服务制作适配器时,此实现将允许该接口在完全不同的上下文中的其他地方使用。
Just wanted to add an example of when you may need to use both. I am currently writing a file handler bound to a database model in a general purpose ERP solution.
This way, I get to have multiple templates for different files and a common set of interface methods with clear distinction. The interface gives the correct analogy to the access methods rather than what would have been with a base abstract class.
Further down the line when I will make adapters for different file storage services, this implementation will allow the interface to be used elsewhere in totally different contexts.