域驱动设计 - 父子关系模式 - 规范模式

时间:2010-01-28 15:57:34

标签: c# java .net domain-driven-design

我想知道在处理父子关系时,以下哪项被认为是最佳做法。

1)以下示例似乎是一种常见做法,但在创建子实例时,只要未添加到父级,它就处于无效状态。这不会导致验证问题等。

public class Parent
{
    private ICollection<Child> children;

    public ReadOnlyCollection Children { get; }

    public void AddChild(Child child)
    {
        child.Parent = this;
        children.Add(child);
    }
}


public class Child
{
    internal Parent Parent
    {
        get;
        set;
    }

    public Child()
    {
    }
}

2)下一个样本会注意孩子必须始终与其父母有关。

public class Parent
{
    private ICollection<Child> children;

    public ReadOnlyCollection Children { get; }

    public Child CreateChild()
    {
        var child = new Child();
        child.Parent = this;
        children.Add(child);
        return child;
    }
}


public class Child
{
    internal Parent Parent
    {
        get;
        set;
    }

    internal Child()
    {
    }
}

3)在最后一个例子中,孩子负责与父母本身的关系。

public class Parent
{
    private ICollection<Child> children;

    public ReadOnlyCollection Children { get; }

    public void AddChild(Child child)
    {
        child.Parent = this;
        children.Add(child);
    }
}


public class Child
{
    public Parent Parent
    {
        get;
        set;
    }

    public Child(Parent parent)
    {
        this.Parent = parent;
    }
}

哪种模式被认为是最好的?我相信模式2可能是最好的,因为如果没有与父母的关系,孩子就永远不会存在。这会使它更容易,例如在实现可能执行以下操作的规范模式时:

public class ChildSpecification
{
    bool IsSatisfiedBy(Child child)
    {
        return child.Parent.Children.Where(someCondition).Count > 0;
    }
}

上述规范只有在孩子有父母的情况下才有效。

你怎么看?你知道更好的方法吗?提前致谢

3 个答案:

答案 0 :(得分:6)

我非常喜欢建议2,但我认为它错过了3中的重要内容,即如果Child对象在没有Parent的情况下不能存在,那么它应该需要{{1}对象在其构造函数中。此外,Parent类上的Parent属性应该是只读的。 所以你最终会得到类似的东西:

Child

答案 1 :(得分:1)

由于我刚刚遇到相同的设计缺陷和问题仍未标记为已回答,我将发布我对解决此问题的看法 - 也许它会帮助任何人。这个解决方案实际上非常适用于NHibernate。

public class Parent
{
    private readonly ISet<Child> _children = new HashedSet<Child> ();
    public virtual IEnumerable<Child> Children { get { return new ImmutableSet<Child> (this._children); }  }


    protected internal virtual void AddChild (Child child)
    {
        this._children.Add(child);
    }
}


public class Child
{
    public virtual Parent Parent { get; protected set; }


    protected Child()
    {
    }


    public static Create (Parent parent)
    {
        if (parent == null)
            throw new ArgumentNullException ("parent");

        var child = new Child
        {
            Parent = parent
        };

        child.Parent.AddChild (child);

        return child;
    }
}

这与#2选项的不同之处在于,使用子对象本身而不是在#2中建议的父对象中收集子对象的创建(并使其初始值无效)。

如果我们使用个人工厂方法(Child.Create)创建子对象,我不确定它是否被认为是不好的设计。

我希望有更多使用DDD经验的人可以对此发表评论。

答案 2 :(得分:0)

我倾向于使用选项(1) - 对我来说一直很好。重要的是不要将子集合本身暴露给外界 - 父母应该能够调解所有访问权限。但我很高兴在其他地方创建一个Child - 我只关心它被添加到Parent中,此时可以检查它的有效性等等。

我不明白你的规范示例:如果父母的孩子的任何的someCondition为true,那么你的ChildSpecification似乎会返回true。肯定 IsSatisfiedBy(Child child)只有在作为参数传递的特定子项满足条件时才返回true。