延迟加载和循环引用
目录
延迟加载
什么是延迟加载?
延迟加载是一种设计模式,它将对象的初始化推迟到实际需要时。应用程序启动时不会加载所有对象,而是按需加载对象,这可以显著提高性能和内存使用率。
主要优点
基本延迟加载实现
让我们从一个简单的例子来理解核心概念:
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 } }