Проект, управляемый доменом – шаблон отношения родительских детей – шаблон спецификации

Мне было интересно, какая из следующих считается лучшей практикой при работе с родительскими отношениями с детьми.

1) Следующий пример представляется обычной практикой, но при создании экземпляра дочернего элемента он будет находиться в недопустимом состоянии, если он не добавлен к родительскому элементу. Не могли ли это привести к проблемам, связанным с проверкой и т. Д.

public class Parent { private ICollection 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 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 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; } } 

Указанная выше спецификация может работать только в том случае, если у ребенка есть родитель.

Как вы думаете? Знаете ли вы лучшие способы? заранее спасибо

Мне определенно нравится предложение номер 2, но я думаю, что он пропускает что-то важное, что находится в 3, а именно, что если объект Child не может существовать без Parent он должен взять объект- Parent в свой конструктор. Кроме того, свойство Parent в classе Child должно быть прочитано только. Таким образом, у вас будет что-то вроде:

 public class Parent { private ICollection 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; } } 

Поскольку я только что столкнулся с теми же проектами, что и вопрос, который еще не отмечен как ответ, я опубликую свое видение решения этой проблемы – возможно, это поможет кому угодно. Это решение действительно отлично подходит для использования с NHibernate.

 public class Parent { private readonly ISet _children = new HashedSet (); public virtual IEnumerable Children { get { return new ImmutableSet (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, мог бы прокомментировать это.

Я предпочитаю использовать вариант (1) – всегда работал хорошо для меня. Важно не подвергать собственную детскую коллекцию внешнему миру – Родитель должен иметь возможность опосредовать весь доступ. Но я совершенно счастлив, что Ребенок будет создан в другом месте. Я только забочусь о нем, когда он добавляется в родителя, и на этом этапе его можно проверить на достоверность и т. Д.

Я не понимаю ваш пример спецификации: кажется, что ваш ChildSpecification вернет true, если у какого-либо из родительских детей есть someCondition как true. Разумеется, IsSatisfiedBy (дочерний ребенок) должен возвращать только true, если конкретный дочерний элемент, переданный как параметр, удовлетворял условию.