工厂模式
本课属于 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 类,你只需更新工厂。所有使用该工厂的代码都能自动使用新类型,无需修改。
挑战
简单让我们使用工厂模式构建一个文档生成系统。不同的文档类型(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"
?>这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: PHP 在线编译器