枚举(通常称为 Enums)是软件开发中的一个强大工具,如果正确使用,它可以在应用程序的架构和设计中发挥重要作用。长期以来,PHP 没有支持这种数据类型,但从 PHP 8.1 开始,我们终于添加了它,这带来了许多可能性。在本文中,让我们回顾如何战略性地使用它们来改进我们的应用程序。
基本上,枚举是一种数据类型,它作为一个预定义常量的集合。例如,如果我们正在构建一个 ToDo 应用程序,我们可以使用 枚举 来表示任务的优先级。
1 enum Priority
2 {
3 case LOW;
4 case NORMAL;
5 case HIGH;
5 }使用 枚举 来表示任务的优先级已经为我们的设计带来了重要的改进,而不是使用字符串来表示它,我们可以将它作为 Task 实体中的类型使用,因此在代码库中处理这个实体时,我们可以轻松识别优先级属性的可能值,而不必猜测或检查数据库。
1 final class Task
2 {
3 public function __construct(
4 public string $title,
5 public string $description,
6 public Priority $priority,
7 ) {
8 }
9 }
10
11 $task = new Task(
12 title: 'My First Task',
13 description: 'Do something',
14 priority: Priority::HIGH,
15 );但是上面的代码在处理数据库时有一个问题,因为 枚举 是由对象表示的,我们无法轻松地将它用于保存数据到数据库中,但我们有另一种类型的 枚举 来拯救局面。
如上所述,枚举 是由对象表示的,但 PHP 还支持 支持后备值的枚举(Backed Enums),它们可以分配一个值。内部它们仍然是由对象表示的,但由于它们是由值表示的,我们可以轻松使用这个值来序列化枚举,例如保存到数据库中。让我们改进我们的 Priority 枚举,并将其转换为 支持后备值的枚举。
1 enum Priority: string
2 {
3 case LOW = 'low';
4 case NORMAL = 'normal';
5 case HIGH = 'high';
6 }Task 实体的实例化可以保持与之前相同,并且仍然有效,但我们也可以以不同的方式使用它。想象一下,我们从 UI 接收优先级作为一个字符串值,并在实例化 Task 实体时需要分配枚举。我们可以使用这种方法:
1 $task = new Task(
2 title: 'My First Task',
3 description: 'Do something',
4 priority: Priority::from($priority),
5 );请记住,我们只能为 支持后备值的枚举 使用 string 或 int 类型。
就像普通类一样,我们也可以在枚举中定义方法,当我们将 match 操作符与这些方法结合使用时,这带来了许多可能性。想象一下,现在我们想为 ToDo 应用程序中的每个优先级分配一个特定的颜色。我们可以通过在 Priority 枚举中创建一个方法来轻松实现这一点。
1 enum Priority: string
2 {
3 case LOW = 'low';
4 case NORMAL = 'normal';
5 case HIGH = 'high';
6
7 public function color(): string
8 {
9 match ($this) {
10 self::LOW => 'green',
11 self::NORMAL => 'blue',
12 self::HIGH => 'red',
13 };
14 }
15 }
16
17 $task = new Task(
18 title: 'My First Task',
19 description: 'Do something',
20 priority: Priority::HIGH,
21 );
22
23 $task->priority->color();
24 // 'red'我们上面看到,我们可以在枚举中定义方法,这为我们的应用程序打开了许多可能性。我们看到的例子只是为优先级返回一个特定的颜色,但我们可以通过这些方法进一步扩展这个边界。
想象一下,我们需要为每个任务向用户发送通知,但每个优先级的通知类型不同。我们有许多不同的方式来处理这种情况,其中一种解决方案是使用我们的 Priority 枚举来获取所需的正确类型的通知发送器。首先,我们需要为通知发送器定义一个契约。
1 interface NotificationSender
2 {
3 public function send(Task $task): void;
4 }现在我们已经定义了契约,我们需要实现我们需要的每种类型的发送器,为这个例子的简单起见,我不会提供这些实现的代码,只列出我们可能拥有的不同实现。
1 final class EmailSender implements NotificationSender
2 {
3 public function send(Task $task): void
4 {
5 // Send email logic here
6 }
7 }
8
9 final class SlackSender implements NotificationSender
10 {
11 public function send(Task $task): void
12 {
13 // Send slack notification logic here
14 }
15 }
16
17 final class PhoneMessageSender implements NotificationSender
18 {
19 public function send(Task $task): void
20 {
21 // Send SMS logic here
22 }
23 }现在有了我们的通知发送器实现,我们可以在 Priority 枚举中实现一个简单的方法,它将为每个优先级类型检索正确的发送器。
1 enum Priority: string
2 {
3 case LOW = 'low';
4 case NORMAL = 'normal';
5 case HIGH = 'high';
6
7 public function notificationSender(): NotificationSender
8 {
9 match ($this) {
10 self::LOW => new EmailSender(),
11 self::NORMAL => new SlackSender(),
12 self::HIGH => new PhoneMessageSender(),
13 };
14 }
15 }使用这个简单的方法,在创建新任务时,我们可以轻松使用下面的方法发送通知。
1 task = new Task(
2 title: 'My First Task',
3 description: 'Do something',
4 priority: Priority::HIGH,
5 );
6
7 $task->priority->notificationSender()->send($task);如你所见,枚举 为我们提供了无限的可能性来改进我们的应用程序,如果我们明智地使用它们,我们可以设计出更干净、更易维护的代码库。我的目标是通过本文展示,我们可以超越简单的辅助函数,如获取颜色或标签,而是使用 枚举 来解决更复杂的问题,并将它们作为应用程序 架构 和 设计 的核心部分。