
假设我们有两个模型类 a 和 b,它们之间存在一对多的关系:a 可以拥有多个 b,而 b 属于一个 a。为了方便访问,我们可能希望 a 对象能直接访问其关联的 b 对象列表,同时 b 对象也能直接访问其所属的 a 对象。
考虑以下简化的构造函数实现:
// 模型 B 的构造函数
class B extends ParentModel
{
    protected $a; // 用于存储关联的 A 对象
    public function __construct(int $id = null)
    {
        parent::__construct($id);
        $aId = $this->get('a_id'); // 从数据库获取关联 A 的ID
        if ($aId) {
            $this->a = new A($aId); // 在 B 的构造函数中实例化 A
        }
    }
}
// 模型 A 的构造函数
class A extends ParentModel
{
    public $B = []; // 用于存储关联的 B 对象列表
    public function __construct(int $id = null)
    {
        parent::__construct($id);
        // 假设 CarbonPL 是一个日期处理类
        $this->date = new CarbonPL($this->get('date'));
        $this->initB(); // 在 A 的构造函数中初始化关联的 B 对象
    }
    private function initB()
    {
        // 检查实例是否存在于数据库
        if (!$this->isReferenced()) {
            return;
        }
        // 构建查询获取所有关联的 B 对象的 ID
        $query = B::getIDQuery();
        $query .= ' WHERE is_del IS FALSE';
        $query .= ' AND a_id = ' . $this->id;
        $ids = Helper::queryIds($query);
        foreach ($ids as $id) {
            $this->B[] = new B($id); // 在 A 的 initB 方法中实例化 B
        }
    }
}从上述代码可以看出,当尝试创建一个 A 对象时,其构造函数会调用 initB 方法,而 initB 方法会遍历数据库中的关联 B 对象ID,并为每个ID创建一个新的 B 对象。当 B 对象的构造函数被调用时,它又会尝试根据 a_id 实例化一个 A 对象。这样,A 实例化 B,B 又实例化 A,形成一个无限循环,导致程序崩溃。
为了解决这种循环依赖和重复实例化的问题,我们可以采用工厂方法模式结合实例缓存机制。其核心思想是:不直接通过 new 关键字创建对象,而是通过一个静态的工厂方法来获取对象实例。这个工厂方法会维护一个内部缓存,如果某个ID对应的对象已经被创建过,就直接从缓存中返回,否则才创建新对象并加入缓存。
以下是 A 类应用工厂方法和实例缓存的示例:
立即学习“PHP免费学习笔记(深入)”;
<?php
class A extends ParentModel
{
    private static $cache = array(); // 静态缓存,存储 A 类的实例
    public $B = []; // 关联的 B 对象列表
    /**
     * 私有构造函数,防止外部直接实例化
     *
     * @param int $id 对象的ID
     */
    private function __construct(int $id)
    {
        parent::__construct($id);
        $this->date = new CarbonPL($this->get('date'));
        $this->initB();
    }
    /**
     * 公共静态工厂方法,用于获取 A 类的实例
     *
     * @param int $id 对象的ID
     * @return A 类的实例
     */
    public static function create_for_id(int $id): A
    {
        // 检查缓存中是否已存在该ID的实例
        if (isset(self::$cache[$id])) {
            return self::$cache[$id];
        } else {
            // 如果不存在,则创建新实例并存入缓存
            $result = new A($id);
            self::$cache[$id] = $result; // 缓存新创建的实例
            return $result;
        }
    }
    private function initB()
    {
        if (!$this->isReferenced()) {
            return;
        }
        $query = B::getIDQuery();
        $query .= ' WHERE is_del IS FALSE';
        $query .= ' AND a_id = ' . $this->id;
        $ids = Helper::queryIds($query);
        foreach ($ids as $id) {
            // 现在通过 B 的工厂方法获取实例,而不是直接 new B()
            $this->B[] = B::create_for_id($id);
        }
    }
}
// 同样,对 B 类也应用相同的模式
class B extends ParentModel
{
    private static $cache = array(); // 静态缓存,存储 B 类的实例
    protected $a; // 关联的 A 对象
    /**
     * 私有构造函数
     *
     * @param int $id 对象的ID
     */
    private function __construct(int $id)
    {
        parent::__construct($id);
        $aId = $this->get('a_id');
        if ($aId) {
            // 现在通过 A 的工厂方法获取实例,而不是直接 new A()
            $this->a = A::create_for_id($aId);
        }
    }
    /**
     * 公共静态工厂方法,用于获取 B 类的实例
     *
     * @param int $id 对象的ID
     * @return B 类的实例
     */
    public static function create_for_id(int $id): B
    {
        if (isset(self::$cache[$id])) {
            return self::$cache[$id];
        } else {
            $result = new B($id);
            self::$cache[$id] = $result;
            return $result;
        }
    }
}现在,无论何时需要 A 或 B 的实例,都应调用 A::create_for_id($id) 或 B::create_for_id($id)。这样,如果一个ID对应的对象已经被创建并存在于缓存中,它将被重用,从而有效地避免了无限循环的发生。
通过采用工厂方法和实例缓存,我们不仅解决了对象循环依赖导致的无限循环实例化问题,还实现了每个唯一ID的对象实例的重用,提高了程序的性能和资源利用率。这种模式在构建复杂对象模型时,尤其是在ORM(对象关系映射)框架中管理关联对象时,非常有用。
以上就是解决PHP对象循环依赖导致的无限循环实例化问题的详细内容,更多请关注php中文网其它相关文章!
 
                        
                        PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
 
                Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号