【问题标题】:Domain Driven Design - Parent child relation pattern - Specification pattern领域驱动设计 - 父子关系模式 - 规范模式
【发布时间】:2010-01-28 15:57:33
【问题描述】:

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

1) 下面的例子看似是一种常见的做法,但是当创建一个child的实例时,只要不添加到parent中就会处于无效状态。这会不会导致验证等问题。

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;
    }
}

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

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

【问题讨论】:

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


    【解决方案1】:

    我绝对喜欢第 2 条建议,但我认为它遗漏了第 3 条中的一些重要内容,即如果没有Parent 就不能存在Child 对象,则它应该在其构造函数中使用Parent 对象.此外,Child 类的 Parent 属性应该是只读的。 所以你最终会得到类似的东西:

    public class Parent 
    { 
        private ICollection<Child> children; 
    
        public ReadOnlyCollection Children { get; } 
    
        public Child CreateChild() 
        { 
            var child = new Child(this); 
            children.Add(child); 
            return child; 
        } 
    } 
    
    
    public class Child 
    { 
        internal Parent Parent 
        { 
           get; 
           private set; 
        } 
    
        internal Child(Parent parent) 
        { 
           this.Parent = parent;
        } 
    } 
    

    【讨论】:

    • 我也会这样做,如果规范是孩子应该总是有一个父母,并且在不知道它的父母的情况下不能被创建。所以,只是 2 和 3 的组合。+1
    【解决方案2】:

    由于我刚刚遇到相同的设计设计和问题,但仍未标记为已回答,我将发布我对解决此问题的看法 - 也许它会帮助任何人。这个解决方案实际上非常适合与 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 经验的人对此发表评论。

    【讨论】:

    • 据我了解,子实体只能从聚合内部引用。这意味着Parent.CreateChild() 将是适当的公共方法,而 Child.Create() 应该只在聚合内部。这是为了确保只有 Parent 可以创建 child,并且所有不变量都由 parent 观察。不过我可能对 DDD 有误解。
    • child.Parent.AddChild (child);: 请不要鼓励这种代码,这会造成巨大的无法控制的混乱。
    【解决方案3】:

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

    我不明白您的规范示例:如果 any 父母的孩子的 someCondition 为 true,您的 ChildSpecification 似乎会返回 true。当然,IsSatisfiedBy(Child child) 应该只在作为参数传递的特定孩子满足条件时才返回 true。

    【讨论】:

    • 规范模式是关于我目前在我的一个项目中遇到的一种特殊情况。一个孩子有一个有效日期范围,它不应该与孩子集合中任何其他孩子的任何其他有效日期范围相交。你会认为这是父母的规范吗?
    • 我可能会在父级的 addChild() 方法中将此作为保护条件来实现。然后,父母将不允许添加,例如通过抛出异常。在这种情况下,我可能不会使用规范。
    • 但我还需要在 UI 上进行检查。所以当抛出异常时,我必须抓住它。这也不是很优雅。而且,规范的好处是我可以在许多不同的场景中使用它,例如 1.) 在我的域中 2.) 在客户端应用程序中预先验证业务逻辑。还是我错了?
    猜你喜欢
    • 1970-01-01
    • 2021-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-17
    • 1970-01-01
    • 2013-08-08
    相关资源
    最近更新 更多