OOP 中的多态性

多态意味着多种形状或多种形式。多态性通过继承 PHP 抽象类以及实现 PHP 接口来实现。

多态性可以帮助你创建一种格式或标准,以编程的方式解决特定的问题,而不是只关注解决方案的单一实现。

我们如何在 PHP 中应用这一功能,使用这一功能有什么好处?让我们以下面小节中的示例代码为例,从 PHP 抽象类开始。

PHP 抽象类的多态性

PHP 中使用抽象类时,我们可以通过使用抽象函数来实现多态性。下面是一个 PHP 抽象类的例子:

AbstractAnimal.php
<?php

namespace Animals\Polymorphism\AbstractExample;

abstract class AbstractAnimal
{
    abstract public function makeSound();
}

理想情况下,每个 PHP 抽象类都应以 Abstract 前缀开头,然后是 PSR 标准建议的抽象类名称。本章后面的 PHP 标准建议(PSR)部分将讨论 PSR 标准。

抽象类至少需要一个声明为抽象的函数。这可以通过在函数的访问修饰符或可见性声明前添加 abstract 关键字来实现,例如 abstract public function makeSound()。现在,我们可能会注意到,在抽象类的 makeSound() 方法中并没有实际的操作或逻辑,正如我们前面解释过的,我们不能实例化抽象类。我们需要子类来扩展抽象类,在子类中我们可以声明该子类要执行的具体操作或逻辑。

下面是一个 Cat.php 子类的示例:

Cat.php
<?php

namespace Animals\Polymorphism\AbstractExample;

class Cat extends AbstractAnimal
{
    public function makeSound(): string
    {
        return "meow!";
    }
}

以下是 Cow.php 子类的示例:

Cow.php
<?php

namespace Animals\Polymorphism\AbstractExample;

class Cow extends AbstractAnimal
{
    public function makeSound(): string
    {
        return "mooo!";
    }
}

这两个类都继承了 AbstractAnimal.php 类,由于我们将 makeSound() 函数声明为抽象方法,Cat.phpCow.php 类也必须具有相同的方法,但不包含抽象关键字。您会注意到,Cat 对象的 makeSound 函数返回的是一个 meow 字符串,而 Cow 对象的类似 makeSound 函数返回的是一个 moo 字符串。在这里,我们通过一个函数签名实现了多态性,并且子类以唯一的方式实现了该函数签名。

接下来,我们将看看如何使用 PHP 接口实现多态性。

PHP 接口的多态性

PHP 接口是 PHP 抽象类的简化版。接口不能像普通类那样具有属性,而且只能包含公开可见的函数。接口中的每个方法都必须由使用接口的类来实现,但不需要添加抽象关键字。因此,在向接口声明函数时,我们必须非常小心。如果接口中的函数过多,很容易导致每个实现类都无法使用这些函数。这就是 接口隔离原则(Interface Segregation Principle) 的作用所在,我们将在 第 8 章 "使用 TDD 和 SOLID 原则" 中对此进行详细讨论。

想象一下,你需要一个程序以不同的格式返回结果,而且你还希望能够隔离逻辑和依赖关系,以得到所需的结果。您可以使用接口来设置对象将遵循的契约。例如,有不同的方式和格式可以返回输出结果,在下面的示例中,我们将返回 XMLJSON

我们将创建一个 PHP 接口,作为 JSONXML 类都将实现的契约。该接口将有一个通用打印函数,接受一个字符串参数并返回一个字符串:

PrinterInterface.php
<?php

namespace Polymorphism\InterfaceExample;

interface PrinterInterface
{
    public function print(string $message): string;
}

然后,我们将创建 PrinterInterface 的第一个具体实现,它必须有 print 函数的具体实现才能返回 JSON 格式的字符串:

Json.php
<?php

namespace Polymorphism\InterfaceExample;

class Json implements PrinterInterface
{
    public function print(string $message): string
    {
        return json_encode(['hello' => $message]);
    }
}

PrinterInterface 的第二个具体实现是 Xml 类——它还必须包含一个返回字符串的 print 函数,但这次字符串将被格式化为 XML

Xml.php
<?php

namespace Polymorphism\InterfaceExample;

class Xml implements PrinterInterface
{
    public function print(string $message): string
    {
        return "<message>Hello " . $message . "</message>";
    }
}

我们在 PrinterInterface 中声明了 public print(string $message): string 方法签名,由于 Xml.phpJson.php 类在类名声明后使用 implements 关键字实现了 PrinterInterface,因此 Xml.phpJson.php 现在都必须遵守契约。它们还必须拥有 public print(string $message): string 函数。每个执行类都有自己独特的输出方式。一个返回 XML,另一个返回 JSON - 一种方法,不同的形式或形状。这就是使用 PHP 接口实现多态性的方法。

然而,使用多态性有什么好处呢?让我们以消费者类为例:

Display.php
<?php

namespace Polymorphism\InterfaceExample;

class Display
{
    /**
    * @var PrinterInterface
    */
    private $printer;

    public function __construct(PrinterInterface $printer)
    {
        $this->setPrinter($printer);
    }

    /**
    * @param string $message
    * @return string
    */
    public function displayOutput(string $message): string
    {
        // Do some additional logic if needed:
        $printerOutput = $this->getPrinter()->print($message);
        $displayOutput = "My Output is: " . $printerOutput;
        return $displayOutput;
    }

    /**
    * @return PrinterInterface
    */
    public function getPrinter(): PrinterInterface
    {
        return $this->printer;
    }

    /**
    * @param PrinterInterface $printer
    */
    public function setPrinter(PrinterInterface $printer): void
    {
        $this->printer = $printer;
    }
}

Display.php 类中,我们有一个 displayOutput 方法,它使用了一个必须实现 PrinterInterface 的对象。displayOutput 方法从实现 PrinterInterface 的对象(我们不知道是什么对象)中获取结果,并将其作为后缀添加到字符串中,然后再返回。

重要的是,Display.php 类不知道 PrinterInterface 实现对象是如何生成 XML 或 JSON 格式的。Display.php 类对此并不关心,也不操心。我们已将责任移交给 PrinterInterface 实现对象。因此,我们只需使用其他对象来实现我们需要它们实现的接口,而不是使用一个包含返回 JSONXML 输出的所有逻辑的神类来造成一团乱麻。Display.php 类甚至不知道使用的是什么类,它只知道使用的对象实现了 PrinterInterface。现在,我们已经成功地将 Display.php 类从格式化 XML、JSON 或其他格式的工作中分离出来。

既然我们已经了解了 PHP OOP 的基本原理,下面我们就来看看如何编写 PHP 代码的一些指南或标准。编写或运行 PHP 程序并不需要这些指南,但它们能帮助开发人员编写出更好、更易读、更可共享的代码。以下关于如何编写 PHP 代码的标准在构建企业级应用程序时非常重要,尤其是当你需要与许多其他开发人员一起开发代码,而且你的代码可能会被使用多年时。最终接手您项目的未来开发人员也应该能够轻松理解、阅读和重用您的代码。以下标准将帮助您和您的团队实现这一目标。