且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

分层对象和自动修复

更新时间:2023-09-11 23:24:28

这是实现层次结构的好方法吗?

我看到它有三个问题,一个是小问题,一个是更严重的问题,另外一个在您的具体情况下显然有问题.

I see three problems with it, one minor, one a little more serious, and one obviously problematic in your concrete situation.

潜在问题:

1..让我们从一个小问题开始,该问题与属性名称及其类型之间的关系有关.我建议一个名为ParentTag的属性本身应为Tag类型.您将其声明为int的事实(就像您对Id所做的一样)表明您应该改为调用属性ParentTagId…或将属性的类型更改为Tag.

1. Let's start with the minor issue, which is about the relationship between properties' names and their types. I would recommend that a property named ParentTag should be of the Tag type itself. The fact that you declared it as int (like you did for Id) suggests that you should call the property ParentTagId instead… or that you change the property's type to Tag.

2..现在来看更严重的问题.我认为Desc指向直接子标记. (如果一个标签可以有多个子标签,则显然您为此属性选择了错误的类型.您需要某种收集.但这是另一个问题.)

2. Now to the more serious issue. I take it that Desc points to an immediate child tag. (In case that one tag can have more than one child tag, you have obviously chosen the wrong type for this property. You'd need some kind of collection. But that's yet another problem.)

如果不注意的话,同时存储父母和孩子的链接很容易导致不一致.因此,***不要为每个标签都具有双向链接,而只存储一个方向上的链接.

Storing both parent and children links can easily lead to inconsistencies if you're not paying good attention. It might therefore be better not to have bi-directional links for each tag, but to only store links going in one direction.

然而,这将使在相反方向上遍历层次结构变得复杂.解决此问题的一种方法是仅存储子链接.如果您随后想找到 T 的父标签,则首先要通过从根标签开始递归遍历层次结构并持续跟踪" T "来找到 T .路径"你正在服用;则父级将成为路径中的倒数第二个标签.

That however will complicate traversing the hierarchy in the opposite direction. One way to solve this problem would be to store only child links; if you then wanted to find the parent tag of T, you'd first find T by recursively traversing the hierarchy starting at the root tag and continuously keep track of the "path" you're taking; the parent will then be the penultimate tag in the path.

3..现在是最直接的问题.异常提示了它:

3. Now to the most immediate issue. The exception hints at it:

Ploeh.AutoFixture.ObjectCreationException […],因为遍历的对象图包含循环引用.

使用当前的Tag实现,可以构建包含循环的标记层次结构.我以为你不想要那个.

With your current implementation of Tag, it's possible to build tag hierarchies that contain cycles. I assume that you don't want that.

例如,标签 C 可以将 P 作为其父标签,尽管 P 已经是 C .因此,如果您开始遵循从 C 开始的ParentTag链,您将首先到达 P ,然后最终回到 C ,如果继续前进,就会陷入无限循环.

For example, tag C can have P as its parent tag, although P is already a child tag of C. Therefore, if you started following the ParentTag chain starting at C, you would first get to P and then eventually arrive back at C, and if you kept going, you would find yourself caught in an infinite loop.

我不了解AutoFixture,但由于类似的原因,它似乎无法处理您的具体标签层次结构.

I don't know AutoFixture, but it seems likely that it cannot deal with your concrete tag hierarchy for similar reasons.

您应该使标签层次结构有向无环图(DAG) -非循环的"在这里很重要.但是,使用当前的Tag类,您可以构建任何有向图;它不能保证不会有任何循环.

You should make your tag hierarchies directed acyclic graphs (DAGs) — the "acyclic" is the important bit here. However, with your current Tag class, you can build any directed graph; it does not guarantee that there won't be any cycles.

防止循环标记层次结构的方法:

1..对ParentTag设置器中的循环进行检查:

1. Implement a check for cycles in the ParentTag setter:

public Tag ParentTag
{
    …
    set
    {
        if (!IsOrIsAncestorOf(value))
        {
            parentTag = value;
        }
        else
        {
            throw new ArgumentException("ParentTag", "would cause a cycle");
        }
    }
}
private Tag parentTag;

private bool IsOrIsAncestorOf(Tag other)
{
    return this == other || IsOrIsAncestorOf(other.Parent));
    //     ^^^^^^^^^^^^^    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    //          Is   …   Or    …    IsAncestorOf
}

2.更简单的是,创建ParentTag readonly,这将迫使您在构造函数中进行设置.这将自动使其无法构建循环标记层次结构-如果您不相信,请尝试一下:

2. Even simpler, make ParentTag readonly, which forces you to set it in the constructor. This will automatically make it impossible to build cyclic tag hierarchies — try it, if you don't believe it:

public Tag(Tag parentTag)
{
    this.parentTag = parentTag;
}

private readonly Tag parentTag;

public Tag ParentTag
{
    get
    {
        return parentTag;
    }
}

我会推荐第二种解决方案.

I would recommend the second solution.