我正在尝试用PHP学习OOP,并且我对接口和抽象类有一些困惑。它们都不包含任何实现,只包含定义,并且应该通过它们的子类来实现。抽象类的哪一部分明确区别于接口?另外,由于它们有明显的相似性,我应该根据什么原因决定使用其中一个?
答案 0 :(得分:7)
可以创建包含具体成员的抽象类,例如方法或属性。您仍然无法直接实例化抽象类,但任何实例化的子类都可以从抽象类中定义的已实现成员中受益。
相比之下,接口从不包含任何实现逻辑。它依赖于每个实现类来提供接口中定义的所有成员的实现。
就我如何查看差异而言,抽象的子类是该类型的类。例如Dog
是Animal
。我将界面视为 do-a 关系。例如ICanDisplayImages
告诉我实现类可以显示图像,但不会告诉我类实际代表什么。
答案 1 :(得分:4)
abstract
类在自身和子类之间形成 is-a 关系,而interface
创建 follow-a 关系。因此,抽象类比接口更具体,它们也可能包含具体实现(例如Template methods),而接口定义实现类必须遵循的契约方法集。这是一个更高级别的抽象,因为实现类不一定是抽象类。用它来标准化你的API。
相关问题:https://stackoverflow.com/search?q=abstract+vs+interface
答案 2 :(得分:3)
它们都不包含任何实现..
抽象类可以实现全部或部分方法。但主要的理念是扩展现有的抽象类,在子类中添加一个新方法(扩展基本功能)。
在子类中,您只能扩展一个类(抽象类)。抽象类定义您必须实现的功能,或者只是通过子类中的其他方法扩展。
接口用于定义类行为。你可以实现多个接口(并说我的子类必须这样做,这个和这个thigs!),但是你只能扩展一个抽象类。
答案 3 :(得分:3)
除了其他答案中描述的OOP哲学之外,我认为抽象类的主要用途是一种骨架。
当您设计应用程序或与团队合作时,它非常有用,因为您有一个基本代码可以使用并扩展它接近界面,但具有更高级的工作流程。
答案 4 :(得分:1)
抽象类和接口之间的主要区别在于接口定义了常见行为,其中抽象类是继承的基础类。换句话说,抽象类定义了子类可能共享的一些核心方法和属性集。考虑一个定义许可证的类。所有许可证都具有某种类型的ID号,并颁发给某个人或组。许可证类可以通过驱动程序许可证类,自行车许可证类和狩猎许可证类扩展,等等。将许可类抽象化的主要原因是它定义了许可的抽象概念。没有许可证这样的东西,所以通过声明类抽象,它不能被实例化 另一方面,接口根本不定义对象。它定义了方法签名。任何实现接口的非抽象类都必须为接口中的所有方法提供实现。这里的优点是该方法提供跨不同类型对象的公共接口,例如,与Strings或任何其他对象一起使用时,compareTo()看起来相同。
答案 5 :(得分:0)
如果方法未定义为抽象,则抽象类可以包含方法实现。如果该方法被定义为抽象,则它不包含实现,但它需要由其继承者实现。抽象类无法实例化,只能继承自,以便继承者允许它使用其行为。
接口只定义方法签名,从中继承的任何类都必须实现接口中包含的所有方法。
答案 6 :(得分:0)
通常,接口用于定义合同,因此您可以进行类型检查。还有一种称为“对接口编程”的编程风格,这是一个好主意。请参阅Dependency Inversion Principle:
一个。高级模块不应该依赖于低级模块。两者都应该取决于抽象。
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
!