1
class foo
{
 private $deps

 public function __construct(bar $obj)
 {
  $this->deps=$obj;
 }
}


class Bar
{
  private $property; //Instance of Bar which have setter and getter methods

}

两者都实现了将对象序列化为 XML 的接口。但是由于交叉引用,它陷入了无限递归。如何解决这个问题?这是我的设计错误吗?如何改进它?

我能想到的一种方法是在序列化之前最后一次清理依赖项。但我不知道这是否是最好的方法。

4

1 回答 1

2

当您的 Serializer 只是简单地遍历对象图时,当您有双向关联时,您将不可避免地遇到无限递归,例如 Article 具有并属于许多 Comments。然后,序列化程序将在文章和评论之间来回遍历。与其说是依赖关系,不如说是关于如何将对象相互关联和链接。

我可以想到三种方法来解决这个问题:

对象跟踪

最简单的解决方案可能是教您的序列化程序记录它已经序列化的对象。为此,请在序列化spl_object_hash之前运行要序列化的每个对象。将该散列存储在一个数组中,当散列已经在记录的列表中时,然后跳过它或插入一个带有指向已插入元素的 idref 属性的元素,例如类似

class Serializer
{
    private $record = array();

    public function serialize($value)
    {
        $hash = spl_object_hash($value);
        if (isset($this->record[$hash])) {
            // skip or insert element with idref to XML
        } else {
            $this->record[$hash] = true;
            // turn $value to XML
        }
    }

// … more code

元数据映射模式

另一种选择是使用等价的MetadataMapping模式进行序列化:

许多处理对象关系映射的代码描述了数据库中的字段如何对应于内存对象中的字段。生成的代码往往是繁琐且重复的编写。元数据映射允许开发人员以简单的表格形式定义映射,然后可以由通用代码处理以执行读取、插入和更新数据的详细信息。

您无需定义哪些属性与数据库中的哪些列匹配,而是定义哪些属性应序列化为 XML。然后,您的序列化程序将检查对象的类型并应用映射中的规则来处理它。显然,您的地图不应包含任何可能导致循环引用的属性。

映射可以像数组一样简单,然后您可以将其传递给序列化器进行配置。这样,您的对象和序列化程序都不需要知道映射如何发生的任何细节。缺点是,您需要定义映射。

访客模式

另一种选择是使用访问者模式,其中序列化程序访问对象图,然后对象有选择地传递要在双调度中序列化的数据。同样,您必须确保只传递不能导致循环引用的数据。缺点是它要求所有涉及的类都接受访问者。

我不会详细介绍此模式,因为它已在网络上广泛介绍。

于 2013-06-21T17:33:00.877 回答