【发布时间】:2011-01-18 07:26:30
【问题描述】:
我正在尝试学习 PHP 中的 OOP,但我对接口和抽象类有些困惑。它们都不包含实现,只包含定义,并且应该通过它们的子类来实现。抽象类的哪一部分清楚地将它们与接口区分开来?此外,由于它们明显的相似之处,我应该基于什么原因决定使用其中一个而不是另一个?
【问题讨论】:
标签: php oop interface abstract-class
我正在尝试学习 PHP 中的 OOP,但我对接口和抽象类有些困惑。它们都不包含实现,只包含定义,并且应该通过它们的子类来实现。抽象类的哪一部分清楚地将它们与接口区分开来?此外,由于它们明显的相似之处,我应该基于什么原因决定使用其中一个而不是另一个?
【问题讨论】:
标签: php oop interface abstract-class
抽象类和接口的主要区别在于接口定义了公共行为,其中抽象类是继承的基础类。换句话说,抽象类定义了子类可能共享的一些核心方法和属性集。考虑一个定义许可证的类。所有许可证都有某种类型的 ID 号,并颁发给某些个人或团体。执照类别可能会被驾驶执照类别、自行车执照类别和狩猎执照类别等扩展。将许可证类抽象化的主要原因是它定义了许可证的抽象概念。没有许可证之类的东西,所以通过声明类抽象,它不能被实例化。
另一方面,接口根本不定义对象。它定义了方法签名。任何实现接口的非抽象类都必须为接口中的所有方法提供实现。这里的优点是该方法提供了跨不同类型对象的通用接口,例如compareTo() 与字符串或任何其他对象一起使用时看起来相同。
【讨论】:
通常使用接口来定义合约,因此您可以进行类型检查。还有一种编程风格叫做“针对接口编程”,这是个好主意。见Dependency Inversion Principle:
A.高级模块不应该依赖于低级模块。两者都应该依赖于抽象。
B.抽象不应依赖于细节。细节应该取决于抽象。”
因此,如果您定义函数和方法,而不是针对类的 type hinting,您只需提示针对接口。
这是一个例子。假设您为输入流和输出流定义接口,如下所示:
interface OutputStream{
write($string); // Writes a string to the output.
close(); // Closes the output stream.
}
interface InputStream{
read($length); // Reads at most $length characters.
eof(); // TRUE, if the input stream is empty.
}
您现在可以创建copy 函数或方法,它将流的完整输出复制到输入,而无需其中任何一个:
// 50 is just chosen randomly.
function copy(InputStream $input, OutputStream $output){
while(!$input->eof()){
$output->write($input->read(50));}}
恭喜,您的 copy 实现现在适用于每个输入和输出流的组合,甚至没有实现一个。
另一方面,抽象类可用于实现通用功能,而无需实现全功能类。
再举个例子。假设您想要输出流。您需要一个方法write($s),它将一个字符串写入输出,并且您需要一个方法writeLine($s),它将字符串和一个额外的换行符写入输出。那么这将是合适的:
abstract class AbstractOutputStream{
public function writeLine($s){
$this->write($s."\n");}}
具体输出流现在可以继承抽象输出流,只需实现write 并免费获得writeLine!
【讨论】:
abstract 类在其自身和子类之间形成 is-a 关系,而 interface 则创建 follows-a 关系。因此,抽象类比接口更具体,它们也可能包含具体的实现(例如Template methods),而接口定义了实现类必须遵循的一组合同方法。这是一个更高级别的抽象,因为实现类不一定是抽象类。使用它来标准化您的 API。
相关问题:https://stackoverflow.com/search?q=abstract+vs+interface
【讨论】:
它们都没有实现..
抽象类可以实现全部或部分方法。但主要理念 - 通过在子类中添加新方法(扩展基本功能)来扩展现有抽象类。
在子类中,您只能扩展一个类(抽象类)。抽象类定义您必须实现或仅由子类中的其他方法扩展的功能。
接口用于定义类行为。你可以实现多个接口(并且说我的子类必须这样做,这个和这个!),但你只能扩展一个抽象类。
【讨论】:
可以创建包含具体成员的抽象类,例如方法或属性。您仍然不能直接实例化抽象类,但任何实例化的子类都可以从抽象类中定义的已实现成员中受益。
相比之下,接口从不包含任何实现逻辑。由每个实现类来提供接口中定义的所有成员的实现。
就我如何看待差异而言,抽象的子类 is-a 该类型的类。例如Dog 是 Animal。我将接口视为 does-a 关系。例如ICanDisplayImages 告诉我实现类可以显示图像,但没有告诉我该类实际代表什么。
【讨论】:
除了其他答案中描述的 OOP 哲学之外,我认为抽象类的主要用途是一种骨架。
当您设计应用程序或与团队合作时,它很有用,因为您有一个基本代码可供使用,并且可以将其扩展为接近界面但具有更高级的工作流程。
【讨论】:
如果方法未定义为抽象,则抽象类可以包含方法实现。如果方法被定义为抽象,则它不包含实现,但需要由其继承者实现。抽象类不能被实例化,只能继承自,以便继承者允许它使用其行为。
一个接口只定义方法签名,任何从它继承的类都必须实现接口中包含的所有方法。
【讨论】: