延迟加载和循环引用
目录
延迟加载
什么是延迟加载?
延迟加载是一种设计模式,它将对象的初始化推迟到实际需要时。应用程序启动时不会加载所有对象,而是按需加载对象,这可以显著提高性能和内存使用率。
主要优点
基本延迟加载实现
让我们从一个简单的例子来理解核心概念:
class User {
private ?Profile $profile = null;
private int $id;
public function __construct(int $id) {
$this->id = $id;
// Notice that Profile is not loaded here
echo "User {$id} constructed without loading profile\n";
}
public function getProfile(): Profile {
// Load profile only when requested
if ($this->profile === null) {
echo "Loading profile for user {$this->id}\n";
$this->profile = new Profile($this->id);
}
return $this->profile;
}
}
class Profile {
private int $userId;
private array $data;
public function __construct(int $userId) {
$this->userId = $userId;
// Simulate database load
$this->data = $this->loadProfileData($userId);
}
private function loadProfileData(int $userId): array {
// Simulate expensive database operation
sleep(1); // Represents database query time
return ['name' => 'John Doe', 'email' => 'john@example.com'];
}
}这个基本实现的工作原理
延迟加载的代理模式
代理模式为延迟加载提供了一种更复杂的方法:
interface UserInterface {
public function getName(): string;
public function getEmail(): string;
}
class RealUser implements UserInterface {
private string $name;
private string $email;
private array $expensiveData;
public function __construct(string $name, string $email) {
$this->name = $name;
$this->email = $email;
$this->loadExpensiveData(); // Simulate heavy operation
echo "Heavy data loaded for {$name}\n";
}
private function loadExpensiveData(): void {
sleep(1); // Simulate expensive operation
$this->expensiveData = ['some' => 'data'];
}
public function getName(): string {
return $this->name;
}
public function getEmail(): string {
return $this->email;
}
}
class LazyUserProxy implements UserInterface {
private ?RealUser $realUser = null;
private string $name;
private string $email;
public function __construct(string $name, string $email) {
// Store only the minimal data needed
$this->name = $name;
$this->email = $email;
echo "Proxy created for {$name} (lightweight)\n";
}
private function initializeRealUser(): void {
if ($this->realUser === null) {
echo "Initializing real user object...\n";
$this->realUser = new RealUser($this->name, $this->email);
}
}
public function getName(): string {
// For simple properties, we can return directly without loading the real user
return $this->name;
}
public function getEmail(): string {
// For simple properties, we can return directly without loading the real user
return $this->email;
}
}代理模式实现
处理循环引用
循环引用带来了特殊的挑战。以下是一个全面的解决方案:
class LazyLoader {
private static array $instances = [];
private static array $initializers = [];
private static array $initializationStack = [];
public static function register(string $class, callable $initializer): void {
self::$initializers[$class] = $initializer;
}
public static function get(string $class, ...$args) {
$key = $class . serialize($args);
// Check for circular initialization
if (in_array($key, self::$initializationStack)) {
throw new RuntimeException("Circular initialization detected for: $class");
}
if (!isset(self::$instances[$key])) {
if (!isset(self::$initializers[$class])) {
throw new RuntimeException("No initializer registered for: $class");
}
// Track initialization stack
self::$initializationStack[] = $key;
try {
$instance = new $class(...$args);
self::$instances[$key] = $instance;
// Initialize after instance creation
(self::$initializers[$class])($instance);
} finally {
// Always remove from stack
array_pop(self::$initializationStack);
}
}
return self::$instances[$key];
}
}
// Example classes with circular references
class Department {
private ?Manager $manager = null;
private string $name;
public function __construct(string $name) {
$this->name = $name;
}
public function setManager(Manager $manager): void {
$this->manager = $manager;
}
public function getManager(): ?Manager {
return $this->manager;
}
}
class Manager {
private ?Department $department = null;
private string $name;
public function __construct(string $name) {
$this->name = $name;
}
public function setDepartment(Department $department): void {
$this->department = $department;
}
public function getDepartment(): ?Department {
return $this->department;
}
}
// Setting up the circular reference
LazyLoader::register(Manager::class, function(Manager $manager) {
$department = LazyLoader::get(Department::class, 'IT Department');
$manager->setDepartment($department);
$department->setManager($manager);
});
LazyLoader::register(Department::class, function(Department $department) {
if (!$department->getManager()) {
$manager = LazyLoader::get(Manager::class, 'John Doe');
// Manager will set up the circular reference
}
});循环引用处理的工作原理
高级实施技术
使用属性实现延迟加载(PHP 8+)
#[Attribute]
class LazyLoad {
public function __construct(
public string $loader = 'default'
) {}
}
class LazyPropertyLoader {
public static function loadProperty(object $instance, string $property): mixed {
// Implementation of property loading
$reflectionProperty = new ReflectionProperty($instance::class, $property);
$attributes = $reflectionProperty->getAttributes(LazyLoad::class);
if (empty($attributes)) {
throw new RuntimeException("No LazyLoad attribute found");
}
// Load and return the property value
return self::load($instance, $property, $attributes[0]->newInstance());
}
private static function load(object $instance, string $property, LazyLoad $config): mixed {
// Actual loading logic here
return null; // Placeholder
}
}