【问题标题】:PHP: what is the use case of abstract classPHP:抽象类的用例是什么
【发布时间】:2015-12-13 17:58:29
【问题描述】:

根据 php.net 文档:

从抽象类继承时,所有在父类声明中标记为抽象的方法都必须由子类定义;此外,这些方法必须定义为具有相同(或较少限制)的可见性。例如,如果抽象方法定义为受保护,则函数实现必须定义为受保护或公共,但不能定义为私有。此外,方法的签名必须匹配,即类型提示和所需参数的数量必须相同。例如,如果子类定义了一个可选参数,而抽象方法的签名没有,则签名中没有冲突。这也适用于 PHP 5.4 的构造函数。 5.4 之前的构造函数签名可能不同。

在 php.net 中,他们展示了以下示例:

abstract class AbstractClass
{
    // Force Extending class to define this method
    abstract protected function getValue();
    abstract protected function prefixValue($prefix);

    // Common method
    public function printOut() {
        print $this->getValue() . "\n";
    }
}

class ConcreteClass1 extends AbstractClass
{
    protected function getValue() {
        return "ConcreteClass1";
    }

    public function prefixValue($prefix) {
        return "{$prefix}ConcreteClass1";
    }
}

class ConcreteClass2 extends AbstractClass
{
    public function getValue() {
        return "ConcreteClass2";
    }

    public function prefixValue($prefix) {
        return "{$prefix}ConcreteClass2";
    }
}

$class1 = new ConcreteClass1;
$class1->printOut();
echo $class1->prefixValue('FOO_') ."\n";

$class2 = new ConcreteClass2;
$class2->printOut();
echo $class2->prefixValue('FOO_') ."\n";

如果我运行它,我会得到以下输出:

ConcreteClass1 FOO_ConcreteClass1 ConcreteClass2 FOO_ConcreteClass2

阅读文档,我最初的理解是“如果父类是扩展的,一般来说父类不能访问子类的方法”,这就是我们需要抽象类的原因。然而,当我尝试上面的相同示例并稍作调整,删除所有抽象(如下所示)时,我看到与前一个示例完全相同的输出。因此,为什么我们需要 PHP 中的抽象类。我发现的唯一用例是在父类中实现接口时;如果我没有将父级声明为抽象,我会收到错误。在其他情况下我必须或强烈建议使用抽象类吗?

//abstract
class AbstractClass
{
    // Force Extending class to define this method
    //abstract protected function getValue();
    //abstract protected function prefixValue($prefix);

    // Common method
    public function printOut() {
        print $this->getValue() . "\n";
    }
}

class ConcreteClass1 extends AbstractClass
{
    protected function getValue() {
        return "ConcreteClass1";
    }

    public function prefixValue($prefix) {
        return "{$prefix}ConcreteClass1";
    }
}

class ConcreteClass2 extends AbstractClass
{
    public function getValue() {
        return "ConcreteClass2";
    }

    public function prefixValue($prefix) {
        return "{$prefix}ConcreteClass2";
    }
}

$class1 = new ConcreteClass1;
$class1->printOut();
echo $class1->prefixValue('FOO_') ."\n";

$class2 = new ConcreteClass2;
$class2->printOut();
echo $class2->prefixValue('FOO_') ."\n";

【问题讨论】:

    标签: php


    【解决方案1】:

    如果我有一组类似的东西,比如说家具。

    class Chair extends Furniture {
        // define methods to handle being sat on
    }
    class Table extends Furniture {
        // define methods to handle holding things up
    }
    

    等等。但是“家具”是一个抽象的概念。它本身并不是像“椅子”和“桌子”那样的事物。因此,最好定义:

    abstract class Furniture {
        // define methods that apply to all kinds of furniture
    }
    

    将类视为“事物的类型”总是一个好主意。有些是具体的,如椅子和桌子,有些是抽象的,如家具。

    关键是,在您的代码中包含new Furniture() 是没有意义的,因为家具应该是一种特定的类型。使用 abstract class 禁止使用 new Furniture() 并充当完整性检查。

    【讨论】:

    • 如果所有抽象类都是关于 class Furniture { protected function __construct() {} } 的,那么抽象类就有意义了;-) 当 // define methods that apply to all kinds of furniture 有很多代码(或高度重复的代码)在抽象上工作时,抽象类才有意义想法但需要具体的实现 + 处理您不一定希望向外界公开的实例状态(在这种情况下,您可以将数据与不同类中的策略分开)。
    • @Niet the Dark Absol,感谢您的回答。我相信这会对很多人有所帮助。但是,我的问题一般不是关于抽象类。在我的问题中,我证明了我们使用抽象所做的事情可以使用简单的类扩展来完成。因此,我想知道抽象类有什么“特别”?
    • @user3360140 我有点认为它与类型提示 (function foo(Bar $baz)) 相同 - 它本身并没有什么不同,没有它你也可以过得很好,但它提供了内置的理智检查,只是不是“不允许在抽象类上实例化”。
    【解决方案2】:

    但是,当我尝试上面的相同示例并稍作调整,删除所有抽象(如下所示)时,我看到的输出与上一个示例完全相同。

    PHP 是动态类型的,因此父类可以访问“可能存在也可能不存在”的方法1;相同的多态性仍然存在(对于非私有方法)。在 Java 或 C# 等语言中,代码将无法编译。 (接口/合同检查是一个单独的问题2。)

    因此,这两个示例在 PHP 中在技术上都是有效的,并且将产生相同的输出,而不会出现错误或警告。

    在其他情况下我必须或强烈建议使用[抽象方法]?

    当应用主格类型/类声明来将方法注释为抽象时“更正确”如下:

    • 确保实现由具体子类提供2

    • 为类型检查工具提供附加信息;如果将来应用更严格的类型检查,这是一种保障

    • 声明方法签名和文档存根;并出现在类型反射中

    链接的示例/文档已经解释了抽象方法的语义目标。 (但是它充满了一些......可疑的措辞;例如private methods cannot be abstract。)


    1 这也称为 duck typing,并且在许多其他支持 OO 和相同概念的动态语言(包括 Ruby、Python 和 JavaScript)中都是允许的通过子类型多态性表达的抽象方法,尽管不那么正式。

    2 接口/契约检查(仅对类型定义进行)确保实现类或抽象类的子类必须符合指定的类型。对于抽象方法,这意味着子类必须提供方法实现or be abstract themselves。

    【讨论】:

      猜你喜欢
      • 2011-02-03
      • 1970-01-01
      • 2017-02-02
      • 2011-01-18
      • 1970-01-01
      • 2016-09-23
      • 2012-11-29
      • 2010-12-03
      • 2017-01-04
      相关资源
      最近更新 更多