Domain Driven Design - Parent child relation pattern - Specification pattern
I was wondering which of the following is considered to be a best practice when dealing with parent child relationships.
1) The following example seems to be a common practice, but when creating an instance of a child, it will be in an invalid state as long as it is not added to the parent. Couldn't this lead to problems regarding validation etc.
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) The next sample would take care that a child must always be related to its parent.
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) In the last example that child takes care of the relation to its parent itself.
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;
}
}
Which pattern is considered the best? I believe that pattern 2 might be the best since then a child can never exist without a relation to its parent. This would make it easier e.g. when implementing a specification pattern that might do things like:
public class ChildSpecification
{
bool IsSatisfiedBy(Child child)
{
return child.Parent.Children.Where(someCondition).Count > 0;
}
}
The above specification can only work if a child has a parent.
What do you think? Do you know better ways? Thanks in advance
I definitely like suggestion number 2, but I think that it misses something important that is found in 3, namely that if a Child
object cannot exist without a Parent
it should take a Parent
object in its constructor. Furthermore the Parent
property on the Child
class should be read only.
So you would end up with something like:
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;
}
}
Since I've just encountered the same design desissions and question still not marked as answered I'll post my vision on solution of this problem - maybe it'll help anyone. This solution actually perfectly viable for use with 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;
}
}
That's differs from your #2 option in a way that creation of the child object (and invalidating it's initial values) are gathered withing child object itself and not in parent object as you suggested in #2.
Tho one thing I'm not sure if it's considered bad design or not if we create child objects with personal factory method (Child.Create
).
I hope someone with more experience in using DDD could comment on that.
I tend to use option (1) - has always worked well for me. The important thing is not to expose the children collection itself to the outside world - the Parent should be able to mediate all the access. But I'm perfectly happy for a Child to be created elsewhere - I only care about it when it gets added to the Parent, and at this point it can be checked for validity etc. etc.
I don't understand your specification example: it seems like your ChildSpecification would return true if any of the parent's children has someCondition as true. Surely IsSatisfiedBy(Child child) should only return true if the specific child passed as a parameter satisfied the condition.
精彩评论