本文目录导读:

在PHP项目中,“蝇量模式”(Flyweight Pattern)和“共享对象”(Shared Object)是两种不同的概念,但它们在解决内存和性能问题上有关联。
🪶 1. 蝇量模式 (Flyweight Pattern)
核心思想
通过共享相同状态的对象来减少内存使用,适用于系统中存在大量细粒度对象、且这些对象可以拆分为内部状态(共享)和外部状态(不共享)的场景。
在 PHP 中的实现
// 1. 定义蝇量接口
interface CharacterFlyweight {
public function render(string $externalState): string;
}
// 2. 共享对象(内部状态:字体样式)
class ConcreteCharacter implements CharacterFlyweight {
private string $fontFamily;
private int $fontSize;
public function __construct(string $fontFamily, int $fontSize) {
$this->fontFamily = $fontFamily;
$this->fontSize = $fontSize;
}
public function render(string $character): string {
return sprintf(
"<span style='font-family:%s; font-size:%dpx'>%s</span>",
$this->fontFamily,
$this->fontSize,
$character
);
}
}
// 3. 工厂(管理共享对象池)
class CharacterFactory {
private array $pool = [];
public function getCharacter(string $fontFamily, int $fontSize): CharacterFlyweight {
$key = md5("{$fontFamily}_{$fontSize}");
if (!isset($this->pool[$key])) {
$this->pool[$key] = new ConcreteCharacter($fontFamily, $fontSize);
}
return $this->pool[$key];
}
}
// 4. 客户端使用
$factory = new CharacterFactory();
$characters = ['A', 'B', 'C', 'A', 'B', 'A'];
// 即使渲染多个字符,字体对象只生成 3 个
$renderResults = [];
foreach ($characters as $char) {
$flyweight = $factory->getCharacter('Arial', 12); // 共享同一个字体对象
$renderResults[] = $flyweight->render($char);
}
📦 2. 共享对象 (Shared Object)
在 PHP 中,“共享对象”可以指不同层面的概念:
a. 对象池模式(Object Pool)
与蝇量模式不同,对象池的核心是复用创建成本高的对象(如数据库连接、大对象)。
class ConnectionPool {
private array $available = [];
private array $inUse = [];
public function getConnection(): PDO {
if (empty($this->available)) {
// 创建新连接(成本高)
$conn = new PDO("mysql:host=localhost;dbname=test", "user", "pass");
} else {
$conn = array_pop($this->available);
}
$this->inUse[spl_object_id($conn)] = $conn;
return $conn;
}
public function release(PDO $conn): void {
$id = spl_object_id($conn);
if (isset($this->inUse[$id])) {
unset($this->inUse[$id]);
$this->available[] = $conn;
}
}
}
b. 共享变量/状态(多进程/多线程)
PHP-FPM 模式下,不同请求之间无法直接共享对象,但可以通过:
- APCu / Redis / Memcached 存储序列化对象
- Swoole 协程 共享内存
- 共享扩展(如
shmop操作共享内存块)
// APCu 示例
apcu_store('shared_config', ['db_host' => 'localhost']);
$config = apcu_fetch('shared_config');
c. 单例模式(进程内共享)
确保一个类只有一个实例,在当前 PHP 进程内共享:
class DBConnection {
private static ?self $instance = null;
private PDO $pdo;
private function __construct() { /* 初始化连接 */ }
public static function getInstance(): self {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
}
🆚 对比:蝇量模式 vs 共享对象
| 特性 | 蝇量模式 | 共享对象(对象池) |
|---|---|---|
| 目的 | 减少内存(对象数量) | 复用创建成本 |
| 核心机制 | 将对象拆分为内部/外部状态 | 预创建 + 循环使用 |
| 适用场景 | 大量相似对象(如字符、粒子) | 昂贵资源(连接、线程) |
| 对象属性 | 不可变内部状态(共享) | 可被重置 |
| 生命周期 | 长期存在(池化管理) | 受池容量限制 |
| PHP 典型例子 | 文本渲染、游戏粒子系统 | 数据库连接池 |
🧠 实战建议
- 使用蝇量模式的前提:对象数量非常大(>1000)且对象可拆分为共享/非共享部分。
- 对象池的应用:在数据库、HTTP客户端、大型计算结果缓存中非常有效。
- 注意线程安全:Swoole 协程环境下共享对象需加锁。
- 性能权衡:共享对象节省了创建时间但增加了管理开销,需要根据业务场景选择合适的实现。
📚 代码示例链接(GitHub)
需要我针对某个具体场景给出更详细的实现吗?