何时在PHP中使用extends/abstract和implements/interface

3
所以,我开始理解PHP中的抽象和接口的概念。但是它真正有用的时候是什么时候呢?当然,我可以使用接口为我的类制定规则,使它们都遵循特定的模式。但是它真的有用吗?为什么我要创建一个抽象类而不是只创建一个可用于其他类的独立类?对于抽象类,我或许能够想到一个好的用途,例如创建一个通用类。比如说创建一个抽象的数据库类,然后将其扩展到Mysql和MsAccess数据库类上。给两者提供相似的函数来操作,从而在两种情况下实现无缝体验。但是,真正有人能给我一个更好的例子来说明抽象和接口何时真正有用吗?请注意,我知道如何编写代码,只是不知道如何使用它。谢谢!

可能是 http://stackoverflow.com/search?q=abstract+vs+interface 的重复问题。 - Gordon
哦,拜托了各位。这不是重复的可能性有多大呢?你们为什么要回答它? - Gordon
1
可能是[Interface vs Abstract Class(general OO)]的重复。 - hakre
感谢@hakre,非常抱歉,我没有找到那些类似的问题。也许是因为我没有将其视为两者之间的战斗,而是两个不同领域中的两件事情。我仍然认为我的问题是一个有效的问题,因为它是在另一种思维方式下设置的。但我很感激提供问题线程的链接以供进一步阅读。 - lejahmie
@jamietelin 你的问题本身是有效的,但它已经被回答过了,这使得根据SO的定义,它是一个重复的问题。这里没有什么特别新颖的东西,所以它应该被关闭。顺便说一句,在搜索时即使不使用“vs”,你也会找到更多可能的重复项:http://stackoverflow.com/search?q=abstract+interface - Gordon
4个回答

8

摘要是指“这里有一个类的模板和一些可以让你开始的代码”。抽象类的设计者可以将某些方法标记为需要由扩展类提供(abstract),或者标记为final,这意味着该类不能覆盖它们。

接口是指“这里有一个类的模板,但你必须自己编写所有的代码”。在接口中声明的任何方法和属性必须由实现接口的类提供。

因此,一个经验法则是:

如果你有可以或必须被子类使用的代码,你需要一个抽象类。如果你只有方法和属性声明,你可以使用一个接口。

不要忘记,使用抽象类会在一定程度上限制你,因为一个子类只能扩展一个类,而它可以实现任意数量的接口。


7
一个接口不是一个类,而抽象类已经是一个类。
由于每个类都有一个接口(根据定义),接口允许您在任何类旁边指定一个接口,无论它是否是抽象的。
然后,针对接口进行编程允许您用另一个类替换一个类,同时保持相同的接口。因此,它使您的代码更少耦合。现在不再针对具体类编程,而是“仅”针对更轻量级的接口编程。如果您不想针对具体类名称编程而是针对类型编程,则接口非常有用。然后,您可以将一个对象替换为实现相同接口的任何其他对象。
另一方面,抽象类 - 即使被称为抽象 - 也非常具体。但是,它不是最终的,因此它形成了一个类的模式,就像规定从中扩展的类应如何编写某个功能一样。由于它是抽象的,所以它不能没有扩展而生存。抽象类用于创建基类,其中包含将多次使用的代码,以减少代码重复并且不能直接实例化。

那么,接口只是一种防止程序员忘记模式的方法吗?可以这样说,它是编码方式的提醒,或者是给未来的编码人员的指示,因为显然他们不会像第一个编码人员一样拥有相同的记忆(如果我们未来没有发明更好的人类记忆共享,也就是说交流的方法)^^ - lejahmie
至少你可以这样使用接口 :) 我不会称其为“遗忘”,而只是为了定义它,指定了接口。当然,这对团队合作很有好处,但也适用于随着时间的推移对具体类进行更改,因为它不是针对某个特定类编码,而是针对接口编码。类可以更改或交换(测试、应用增长)。一些程序员在编写类之前总是使用接口,但据我所知,这有时是多余的,因为并非必要。只是说一下。 - hakre

2

接口就像一个合同。如果一个类实现了一个接口,使用这个类的其他代码现在知道它支持某些功能。

PHP中最好的接口示例是Iterator接口。

接口的好处是类可以实现多个接口。'扩展'不允许这样做。这意味着子类可以实现一个接口,但它的父类不必。

阅读设计模式。你会发现很多内容都有涉及,并且你再也不会怀疑什么情况下使用它是有意义的。我认为《Head first设计模式》是一本很好的书,写得非常好。虽然他们在例子中使用的是Java,但大部分内容在PHP中也是适用的。

我还有一个真实场景的例子。

我们的应用程序总是在出现错误时无处不在地抛出异常。每种类型的异常都有自己的自定义类。其中一个例子是RecordNotFound异常。

如果异常没有被捕获,就会有一个顶级异常块,它看起来有点像这样:

try {

 // Do everything in the app!

} catch (Exception $e) {

  // draw a good error page

}

在某些情况下,异常需要映射到特定的HTTP状态码,例如404(未找到)。因此我们有了这个接口:
interface HTTPException {

  function getHTTPCode();

}

任何异常,无论在继承树中深度如何,现在都可以实现此接口并发出特定的HTTP状态码。

我甚至创建了没有任何方法的接口。我会留给你思考为什么这可能有意义。


标记接口模式?!那么,是一种将类分组的方式? - lejahmie

2

接口是一个类的骨架结构,它本身不是一个类。我们可以实现该类并添加一些额外的特性以使其更加清晰。在接口中,可以使用implements关键字来实现。通常情况下,实现是用于接口实现功能的方式。

抽象是隐藏一些功能并仅显示必要功能的类。可以使用extend关键字来扩展抽象类。通常情况下,扩展用于扩展抽象类的功能。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接