构造函数中的无限循环 - 创建相关对象的相同实例
Infinty loop in constuctor - Creating the same istances of related objects
我陷入了在模型中一遍又一遍地创建相同对象的无限循环。
但从头开始:
我有两个相关的模型,我们叫A
和B
A
可以有很多 B
而 B
属于一个 A
.
并且由于模型 B
引用 A
的字段,我将其添加为受保护的 属性 以避免在调用与 [ 相关的 B
方法时创建新对象=14=] 字段。
B
的构造函数如下所示:
public function __construct(int $id = null)
{
parent::__construct($id);
$a = $this->get('a_id');
if ($a) {
$this->a = new A($a);
}
}
和A
:
public function __construct(int $id = null)
{
parent::__construct($id);
$this->date = new CarbonPL($this->get('date'));
$this->initB();
}
但后来我意识到调用 initB() 会再次创建相同的实例 o B 并创建相同的 A 等等
private function initB()
{
if (!$this->isReferenced()) { // Checks wheter is instance exists in DB
return;
}
$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 的对象包含 B 的加载,反之亦然,因为很多 B 引用 A 字段,反之亦然,但是如何防止自己陷入这种无限循环。
我很快想到 B
中的一个额外参数,即 A
,如果是,我就不必重用构造函数了。但我不太喜欢这个解决方案,因为第二个参数,我想知道如何以不同(更好)的方式解决它,最好是这样你仍然只能输入标识符。
B
修复后的构造函数:
public function __construct(int $id = null, A $a = null)
{
parent::__construct($id);
if ($a) {
$this->a = $a;
} else {
$a = $this->geti('a_id');
if ($a) {
$this->a = new A($a);
}
}
}
您提出的 2 参数解决方案没有任何问题,但如果您正在寻找其他选择...
解决这个问题的一种方法是为每个 class 创建一个实例缓存,这样当有人需要您的 class 实例时,他们不会调用 new()
他们仅当缓存中不存在具有该 ID 的实例时,才调用将构造新实例的工厂方法。
<?php
class A {
private static $cache = array();
private function __construct( $id ) {
// A private constructor prevents any other class from creating new instances
// It could be protected if you want to allow subclasses to call new A()
parent::__construct( $id );
$this->date = new CarbonPL($this->get('date'));
$this->initB();
}
public static function create_for_id( $id ) {
if ( isset( self::$cache[ $id ] ) ) {
$result = self::$cache[ $id ];
} else {
$result = new A( $id );
self::$cache[ $id ] = $result; // Save it for later reuse
}
return $result;
}
}
在 class B 中,您可以做同样的事情,或者它最适合您的情况。
现在在您的 B 构造函数中(或任何您想要新 A 的地方)而不是调用 new A($id)
,您将调用 A::create_for_id( $id )
。如果具有该 ID 的 A 已经存在,那么它将重用它,而不是构造无穷无尽的新实例。
我陷入了在模型中一遍又一遍地创建相同对象的无限循环。
但从头开始:
我有两个相关的模型,我们叫A
和B
A
可以有很多 B
而 B
属于一个 A
.
并且由于模型 B
引用 A
的字段,我将其添加为受保护的 属性 以避免在调用与 [ 相关的 B
方法时创建新对象=14=] 字段。
B
的构造函数如下所示:
public function __construct(int $id = null)
{
parent::__construct($id);
$a = $this->get('a_id');
if ($a) {
$this->a = new A($a);
}
}
和A
:
public function __construct(int $id = null)
{
parent::__construct($id);
$this->date = new CarbonPL($this->get('date'));
$this->initB();
}
但后来我意识到调用 initB() 会再次创建相同的实例 o B 并创建相同的 A 等等
private function initB()
{
if (!$this->isReferenced()) { // Checks wheter is instance exists in DB
return;
}
$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 的对象包含 B 的加载,反之亦然,因为很多 B 引用 A 字段,反之亦然,但是如何防止自己陷入这种无限循环。
我很快想到 B
中的一个额外参数,即 A
,如果是,我就不必重用构造函数了。但我不太喜欢这个解决方案,因为第二个参数,我想知道如何以不同(更好)的方式解决它,最好是这样你仍然只能输入标识符。
B
修复后的构造函数:
public function __construct(int $id = null, A $a = null)
{
parent::__construct($id);
if ($a) {
$this->a = $a;
} else {
$a = $this->geti('a_id');
if ($a) {
$this->a = new A($a);
}
}
}
您提出的 2 参数解决方案没有任何问题,但如果您正在寻找其他选择...
解决这个问题的一种方法是为每个 class 创建一个实例缓存,这样当有人需要您的 class 实例时,他们不会调用 new()
他们仅当缓存中不存在具有该 ID 的实例时,才调用将构造新实例的工厂方法。
<?php
class A {
private static $cache = array();
private function __construct( $id ) {
// A private constructor prevents any other class from creating new instances
// It could be protected if you want to allow subclasses to call new A()
parent::__construct( $id );
$this->date = new CarbonPL($this->get('date'));
$this->initB();
}
public static function create_for_id( $id ) {
if ( isset( self::$cache[ $id ] ) ) {
$result = self::$cache[ $id ];
} else {
$result = new A( $id );
self::$cache[ $id ] = $result; // Save it for later reuse
}
return $result;
}
}
在 class B 中,您可以做同样的事情,或者它最适合您的情况。
现在在您的 B 构造函数中(或任何您想要新 A 的地方)而不是调用 new A($id)
,您将调用 A::create_for_id( $id )
。如果具有该 ID 的 A 已经存在,那么它将重用它,而不是构造无穷无尽的新实例。