Menu
CoddyTech

工厂模式

本课属于 Coddy PHP 学习旅程的“面向对象编程”单元。第 72 课,共 91 课。

工厂模式是一种创建型设计模式,它将对象的创建委托给单独的类或方法。你不必在代码中到处直接使用 new,而是让工厂为你创建对象。

当你有多个相关类共享同一个接口,并且想根据某个条件决定实例化哪一个类时,这种模式特别有用。假设有一个通知系统,可以通过不同的渠道发送消息:

<?php
interface Notification {
    public function send(string $message): string;
}

class EmailNotification implements Notification {
    public function send(string $message): string {
        return "Email: $message";
    }
}

class SmsNotification implements Notification {
    public function send(string $message): string {
        return "SMS: $message";
    }
}

如果没有工厂,你就会到处散落 if 语句和 new 调用。工厂将这些逻辑集中起来:

<?php
class NotificationFactory {
    public static function create(string $type): Notification {
        return match($type) {
            'email' => new EmailNotification(),
            'sms' => new SmsNotification(),
            default => throw new InvalidArgumentException("Unknown type: $type")
        };
    }
}

$notification = NotificationFactory::create('email');
echo $notification->send('Hello!');

输出:

Email: Hello!

主要优点是,你的代码依赖于 Notification 接口,而不是具体的类。如果之后添加 PushNotification 类,你只需更新工厂。所有使用该工厂的代码都能自动使用新类型,无需修改。

challenge icon

挑战

简单

让我们使用工厂模式构建一个文档生成系统。不同的文档类型(PDF、HTML、纯文本)共享一个通用接口,但会生成不同的输出:这正适合通过工厂集中创建对象。

你将把代码组织在 4 个文件中:

  • Document.php:创建一个 Document 接口,定义所有文档类型都必须遵循的约定。它应包含一个方法 render(string $content): string,接收内容并返回格式化后的文档输出。
  • Documents.php:引入 Document 接口,并创建 3 个实现该接口的类:
    • PdfDocument:它的 render() 方法返回 "PDF: [content]"
    • HtmlDocument:它的 render() 方法返回 "<html>[content]</html>"
    • TextDocument:它的 render() 方法返回 "TXT: [content]"
  • DocumentFactory.php:引入 Documents 文件,并创建一个 DocumentFactory 类,其中包含静态方法 create(string $type): Document。此方法应使用 match 根据类型返回相应的文档对象:
    • "pdf" 返回一个新的 PdfDocument
    • "html" 返回一个新的 HtmlDocument
    • "text" 返回一个新的 TextDocument
    • 其他类型应抛出 InvalidArgumentException,错误消息为 "Unknown document type: [type]"
  • main.php:引入 DocumentFactory 文件。你会收到两个输入:文档类型和要渲染的内容。

    使用工厂创建相应的文档类型,然后调用它的 render() 方法并传入内容,再打印结果。

    在新的一行中,打印 "Factory benefit: Adding new types only requires updating the factory",以突出这种模式的价值。

工厂模式能让你的代码保持灵活。如果之后需要添加 MarkdownDocument,你只需更新工厂,所有现有代码都能继续正常工作,无需改动。

亲手试试

<?php
require_once 'DocumentFactory.php';

// 读取输入
$type = trim(fgets(STDIN));
$content = trim(fgets(STDIN));

// TODO:使用 DocumentFactory 创建合适的文档类型
// TODO:将内容传给 render() 方法,并打印结果
// TODO:另起一行打印 "Factory benefit: Adding new types only requires updating the factory"

?>
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: PHP 在线编译器