PHP:抽象类的用例是什么

PHP:抽象类的用例是什么,php,Php,根据php.net文档: 从抽象类继承时,父类声明中标记为抽象的所有方法必须由子类定义;此外,这些方法必须以相同(或限制较少)的可见性定义。例如,如果抽象方法定义为protected,则函数实现必须定义为protected或public,而不是private。此外,方法的签名必须匹配,即类型提示和所需参数的数量必须相同。例如,如果子类定义了可选参数,而抽象方法的签名没有,则签名中没有冲突。这也适用于PHP5.4中的构造函数。在5.4之前,构造函数签名可能会有所不同 在php.net中,他们展示了

根据php.net文档:

从抽象类继承时,父类声明中标记为抽象的所有方法必须由子类定义;此外,这些方法必须以相同(或限制较少)的可见性定义。例如,如果抽象方法定义为protected,则函数实现必须定义为protected或public,而不是private。此外,方法的签名必须匹配,即类型提示和所需参数的数量必须相同。例如,如果子类定义了可选参数,而抽象方法的签名没有,则签名中没有冲突。这也适用于PHP5.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";

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

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
}
把类看作“事物的类型”总是一个好主意。有些是具体的,像椅子和桌子,还有一些是抽象的,像家具

class Chair extends Furniture {
    // define methods to handle being sat on
}
class Table extends Furniture {
    // define methods to handle holding things up
}
关键是,在代码中添加
新家具()
是没有意义的,因为家具应该是一种特定的类型。使用
抽象类
不允许
新建家具()
,并充当健全性检查

然而,当我尝试上面的同一个示例时,稍微做了一些调整,删除了所有的抽象(如下所示),我看到的输出与前面的示例完全相同

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

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

是否有其他情况我必须使用或强烈建议使用[抽象方法]

当应用命名类型/类声明将方法注释为抽象时,“更正确”,如下所示:

  • 建立一个保证,保证实现由具体的子类2提供

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

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

链接的示例/文档已经解释了抽象方法的语义目标。(然而,它充满了一些……有问题的措辞;例如……)


1这也被称为duck类型,在许多其他动态语言中都是允许的,包括Ruby、Python和JavaScript,这些语言支持OO和相同的抽象方法概念,尽管形式上不太正式,但通过子类型多态性表示


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

如果是这样,那么所有抽象类都是关于
类家具{protectedfunction\uu construct(){}
就可以了;-)当
//定义适用于各种家具的方法时,抽象类是有意义的
是大量代码(或高度重复的代码),这些代码在抽象概念上工作,但需要具体的实现+在实例的状态下工作,您不必向外界公开(在这种情况下,您可以在不同的类中将数据与策略分开).@Niet the Dark Absol,谢谢你的回答。我相信这对很多人都有帮助。但是,我的问题不是一般的抽象类。在我的问题中,我演示了,我们使用抽象所做的事情可以使用简单的类扩展来完成。因此,我想知道什么是“特殊的”关于抽象类?@user3360140我有点认为它与类型暗示相同(
function foo(Bar$baz)
)-它本身没有什么不同,没有它你也可以过得很好,但它提供了一个内置的健全性检查,而不是“不允许在抽象类上实例化”。