关于php:抽象类的目的是什么?

What is the purpose of abstract classes?

我正在尝试用PHP学习OOP,我对接口和抽象类有些困惑。它们都不包含实现,只包含定义,应该通过子类实现。抽象类的哪一部分清楚地将它们与接口区分开来?而且,由于它们明显的相似性,基于什么原因,我应该决定使用一个而不是另一个?


可以创建包含具体成员的抽象类,如方法或属性。您仍然不能直接实例化抽象类,但是任何已实例化的子类都可以从抽象类中定义的已实现成员中受益。

相比之下,接口从不包含任何实现逻辑。由每个实现类来提供接口中定义的所有成员的实现。

就我如何看待这些差异而言,抽象的子类就是这种类型的类。例如,DogAnimal。我把一个接口看作是一种Do-A关系。例如,ICanDisplayImages告诉我,实现类可以显示图像,但对类的实际表示却一无所知。


一个abstract类在其自身和子类之间形成了一种is关系,而一个interface类则形成了一种follow-a关系。因此,抽象类比接口更具体,它们也可能包含具体的实现(例如模板方法),而接口定义了实现类必须遵循的方法的契约集。这是一个更高级别的抽象,因为实现类不一定是抽象类。使用它来标准化您的API。

相关问题:https://stackoverflow.com/search?Q=抽象+vs+接口


除了其他答案中描述的OOP哲学之外,我认为抽象类的主要用途是某种框架。

它在设计应用程序或与团队合作时很有用,因为您有一个基本代码可以使用并扩展它接近接口,但使用更高级的工作流。


They both contain no implementations..

抽象类可以实现全部或部分方法。但主要的哲学是通过在子类中添加新方法(扩展基本功能)来扩展现有的抽象类。

在子类中,只能扩展一个类(抽象类)。抽象类定义了必须实现的功能,或者只是由子类中的其他方法扩展的功能。

接口用于定义类行为。您可以实现多个接口(并说我的子类必须做到这一点、这一点和这一点!),但只能扩展一个抽象类。


抽象类和接口之间的主要区别是接口定义了公共行为,其中抽象类是继承的基础类。换句话说,抽象类定义了子类可能共享的一些核心方法和属性集。考虑定义许可证的类。所有许可证都具有某种类型的ID号,并颁发给某些个人或组。许可证类别可以通过驾驶证类别、自行车许可证类别和狩猎许可证类别等进行扩展。将许可证类抽象化的主要原因是它定义了许可证的抽象概念。没有许可证这样的东西,所以通过声明类抽象,它不能被实例化。另一方面,接口根本不定义对象。它定义了方法签名。实现接口的任何非抽象类都必须为接口中的所有方法提供实现。这里的优点是,该方法提供了跨不同类型对象的公共接口,例如,当与字符串或任何其他对象一起使用时,compareto()看起来相同。


通常,接口用于定义契约,因此您可以进行类型检查。还有一种称为"针对接口编程"的编程风格,这是一个好主意。参见依赖倒置原理:

A. High-level modules should not depend on low-level modules. Both should depend on abstractions.

B. Abstractions should not depend upon details. Details should depend upon abstractions."

因此,如果您定义函数和方法,而不是对类进行类型提示,那么您只需要对接口进行提示。

下面是一个例子。例如,您定义输入流和输出流的接口如下:

1
2
3
4
5
6
7
8
9
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函数或方法,它将流的完整输出复制到输入,而不需要任何函数或方法:

1
2
3
4
// 50 is just chosen randomly.
function copy(InputStream $input, OutputStream $output){
  while(!$input->eof()){
    $output->write($input->read(50));}}

祝贺您,您的copy实现现在适用于输入流和输出流的每个组合,甚至不需要实现一个。

另一方面,抽象类可以用来实现公共功能,而不必实现完全功能类。

再举一个例子。比如说,您希望有输出流。您需要一个方法write($s),它向输出写入一个字符串,您需要一个方法writeLine($s),它向输出写入字符串和一个额外的换行符。那么这是适当的:

1
2
3
4
abstract class AbstractOutputStream{
  public function writeLine($s){
    $this->write($s."
"
);}}

具体的输出流现在可以从抽象的输出流继承,只实现write并免费获得writeLine


如果方法未定义为抽象的,则抽象类可以包含方法实现。如果方法被定义为抽象的,则它不包含实现,但需要由其继承者实现。抽象类不能实例化,但只能从继承者继承,以便继承者使用其行为。

接口只定义方法签名,继承它的任何类必须实现接口中包含的所有方法。