OOP 中的多态性
多态意味着多种形状或多种形式。多态性通过继承 PHP 抽象类以及实现 PHP 接口来实现。
多态性可以帮助你创建一种格式或标准,以编程的方式解决特定的问题,而不是只关注解决方案的单一实现。
我们如何在 PHP 中应用这一功能,使用这一功能有什么好处?让我们以下面小节中的示例代码为例,从 PHP 抽象类开始。
PHP 抽象类的多态性
在 PHP 中使用抽象类时,我们可以通过使用抽象函数来实现多态性。下面是一个 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
子类的示例:
<?php
namespace Animals\Polymorphism\AbstractExample;
class Cat extends AbstractAnimal
{
public function makeSound(): string
{
return "meow!";
}
}
以下是 Cow.php
子类的示例:
<?php
namespace Animals\Polymorphism\AbstractExample;
class Cow extends AbstractAnimal
{
public function makeSound(): string
{
return "mooo!";
}
}
这两个类都继承了 AbstractAnimal.php
类,由于我们将 makeSound()
函数声明为抽象方法,Cat.php
和 Cow.php
类也必须具有相同的方法,但不包含抽象关键字。您会注意到,Cat
对象的 makeSound
函数返回的是一个 meow
字符串,而 Cow
对象的类似 makeSound
函数返回的是一个 moo
字符串。在这里,我们通过一个函数签名实现了多态性,并且子类以唯一的方式实现了该函数签名。
接下来,我们将看看如何使用 PHP 接口实现多态性。
PHP 接口的多态性
PHP 接口是 PHP 抽象类的简化版。接口不能像普通类那样具有属性,而且只能包含公开可见的函数。接口中的每个方法都必须由使用接口的类来实现,但不需要添加抽象关键字。因此,在向接口声明函数时,我们必须非常小心。如果接口中的函数过多,很容易导致每个实现类都无法使用这些函数。这就是 接口隔离原则(Interface Segregation Principle) 的作用所在,我们将在 第 8 章 "使用 TDD 和 SOLID 原则" 中对此进行详细讨论。
想象一下,你需要一个程序以不同的格式返回结果,而且你还希望能够隔离逻辑和依赖关系,以得到所需的结果。您可以使用接口来设置对象将遵循的契约。例如,有不同的方式和格式可以返回输出结果,在下面的示例中,我们将返回 XML 和 JSON。
我们将创建一个 PHP 接口,作为 JSON 和 XML 类都将实现的契约。该接口将有一个通用打印函数,接受一个字符串参数并返回一个字符串:
<?php
namespace Polymorphism\InterfaceExample;
interface PrinterInterface
{
public function print(string $message): string;
}
然后,我们将创建 PrinterInterface
的第一个具体实现,它必须有 print
函数的具体实现才能返回 JSON 格式的字符串:
<?php
namespace Polymorphism\InterfaceExample;
class Json implements PrinterInterface
{
public function print(string $message): string
{
return json_encode(['hello' => $message]);
}
}
PrinterInterface
的第二个具体实现是 Xml
类——它还必须包含一个返回字符串的 print
函数,但这次字符串将被格式化为 XML:
<?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.php
和 Json.php
类在类名声明后使用 implements
关键字实现了 PrinterInterface
,因此 Xml.php
和 Json.php
现在都必须遵守契约。它们还必须拥有 public print(string $message): string
函数。每个执行类都有自己独特的输出方式。一个返回 XML
,另一个返回 JSON
- 一种方法,不同的形式或形状。这就是使用 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
实现对象。因此,我们只需使用其他对象来实现我们需要它们实现的接口,而不是使用一个包含返回 JSON 或 XML 输出的所有逻辑的神类来造成一团乱麻。Display.php
类甚至不知道使用的是什么类,它只知道使用的对象实现了 PrinterInterface
。现在,我们已经成功地将 Display.php
类从格式化 XML、JSON 或其他格式的工作中分离出来。
既然我们已经了解了 PHP OOP 的基本原理,下面我们就来看看如何编写 PHP 代码的一些指南或标准。编写或运行 PHP 程序并不需要这些指南,但它们能帮助开发人员编写出更好、更易读、更可共享的代码。以下关于如何编写 PHP 代码的标准在构建企业级应用程序时非常重要,尤其是当你需要与许多其他开发人员一起开发代码,而且你的代码可能会被使用多年时。最终接手您项目的未来开发人员也应该能够轻松理解、阅读和重用您的代码。以下标准将帮助您和您的团队实现这一目标。