【问题标题】:What is the purpose of abstract classes?抽象类的目的是什么?
【发布时间】:2011-01-18 07:26:30
【问题描述】:

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

【问题讨论】:

    标签: php oop interface abstract-class


    【解决方案1】:

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

    【讨论】:

    • 你还可以添加一些代码示例,使其更清晰。
    【解决方案2】:

    通常使用接口来定义合约,因此您可以进行类型检查。还有一种编程风格叫做“针对接口编程”,这是个好主意。见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!

    【讨论】:

      【解决方案3】:

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

      相关问题:https://stackoverflow.com/search?q=abstract+vs+interface

      【讨论】:

        【解决方案4】:

        它们都没有实现..

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

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

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

        【讨论】:

          【解决方案5】:

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

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

          就我如何看待差异而言,抽象的子类 is-a 该类型的类。例如Dog 是 Animal。我将接口视为 does-a 关系。例如ICanDisplayImages 告诉我实现类可以显示图像,但没有告诉我该类实际代表什么。

          【讨论】:

          • 如果一个类没有实现,它对它的子类有什么明确的用途......?
          • 它标准化了实现的外观。例如,数据库适配器接口可能会定义您必须实现的方法。用户可以只用另一个适配器替换一个适配器,并期望它能够工作,因为接口是相同的。
          • 此外,与任何可以被子类化的类一样,它可以为所有子类提供功能,而无需子类自己提供。
          【解决方案6】:

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

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

          【讨论】:

            【解决方案7】:

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

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

            【讨论】:

              猜你喜欢
              • 2015-06-12
              • 1970-01-01
              • 2012-03-22
              • 2011-02-03
              • 1970-01-01
              • 2019-11-19
              • 2015-12-13
              • 1970-01-01
              • 2021-08-20
              相关资源
              最近更新 更多