Проект, управляемый доменом. Паттерн родительских и дочерних отношений. Паттерн спецификации - PullRequest
6 голосов
/ 28 января 2010

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

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 ]

6 голосов
/ 28 января 2010

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

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;
    } 
} 
1 голос
/ 12 июня 2012

Поскольку я только что столкнулся с теми же конструктивными решениями и вопросом, который все еще не помечен как ответивший, я опубликую свое видение решения этой проблемы - возможно, это кому-нибудь поможет. Это решение на самом деле идеально подходит для использования с 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 мог бы прокомментировать это.

0 голосов
/ 28 января 2010

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

...