单例模式
本课属于 Coddy PHP 学习旅程的“面向对象编程”单元。第 71 课,共 91 课。
单例模式是一种创建型设计模式,可确保一个类在应用程序的整个生命周期中只有一个实例。它还提供了一个全局访问点,用于访问该实例。
当你需要一个对象来协调整个系统中的操作时,这种模式很有用,例如数据库连接、配置管理器或日志记录器。创建多个这样的实例会浪费资源,或导致行为不一致。
下面介绍如何在 PHP 中实现单例:
<?php
class Database {
private static ?Database $instance = null;
private function __construct() {
// 私有构造函数可防止直接创建实例
}
public static function getInstance(): Database {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function query(string $sql): string {
return "Executing: $sql";
}
}
$db1 = Database::getInstance();
$db2 = Database::getInstance();
echo $db1 === $db2 ? "Same instance" : "Different instances";
输出:
Same instance这种模式依赖三个关键元素:一个私有构造函数,用于阻止直接使用 new;一个静态属性,用于保存唯一实例;以及一个静态方法,用于在首次调用时创建实例,并在后续调用时返回该实例。
为了让 Singleton 真正万无一失,你还应该阻止克隆和反序列化:
<?php
private function __clone() {}
public function __wakeup() {
throw new Exception("Cannot unserialize singleton");
}
挑战
简单让我们使用单例模式构建一个配置管理器。在实际应用中,你通常需要一个统一的配置设置来源:存在多个配置对象可能会导致系统各处的行为不一致。
你将把代码分成两个文件:
Config.php:创建一个实现单例模式的Config类。你的配置管理器应当:- 将设置存储在一个私有数组属性中
- 使用私有构造函数,并将设置数组初始化为空
- 提供一个静态
getInstance()方法,用于返回唯一实例 - 将
__clone()设为私有,防止克隆 - 包含一个
set(string $key, string $value): void方法,用于存储设置 - 包含一个
get(string $key): ?string方法,用于返回指定键对应的值;如果键不存在,则返回null - 包含一个
getAll(): array方法,用于返回所有设置
main.php:引入 Config 文件。你会收到两个输入:一个键和一个值。获取 Config 实例,并设置一个默认项,键为
"app_name",值为"MyApp"。再次调用
getInstance(),获取 Config 实例的另一个引用,并使用这个引用,根据输入的键和值设置另一项。最后,验证单例是否正常工作:
- 根据两个引用是否指向同一个对象,打印
"Same instance: true"或"Same instance: false"(使用===进行比较) - 打印
"Settings count: [count]",显示存储的设置总数(对getAll()的结果使用count()) - 打印
"App name: [value]",显示通过第二个引用获取的 app_name 设置
- 根据两个引用是否指向同一个对象,打印
这个挑战展示了单例模式的核心优势:无论你调用 getInstance() 多少次,得到的始终是同一个对象,从而确保应用的所有部分共享相同的配置状态。
亲手试试
<?php
require_once 'Config.php';
// 读取输入
$key = trim(fgets(STDIN));
$value = trim(fgets(STDIN));
// TODO: 获取 Config 实例,并将 "app_name" 设置为 "MyApp"
// TODO: 再次调用 getInstance(),获取 Config 的第二个引用
// TODO: 使用第二个引用,根据输入的 $key 和 $value 设置配置项
// TODO: 验证 Singleton 是否正常工作:
// 根据 === 比较结果,输出 "Same instance: true" 或 "Same instance: false"
// 输出 "Settings count: [count]",显示配置项总数
// 输出 "App name: [value]",显示通过第二个引用获取的 app_name
?>这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: PHP 在线编译器