2010-03-06 4 views
9

У меня есть класс Person и два унаследованных класса, называемых родительскими и дочерними. Родитель может иметь n Ребенок (ы), а ребенок может иметь n родительских (ых) родителей.Как перекрестно ссылаться на объекты в классах

Каков наилучший способ в OOD создать ссылку между родителем и ребенком.

Должен ли я создавать список в каждом классе, ссылаясь на подключенного родителя/ребенка или есть лучший способ?

ответ

11

Большой вопрос.Чистые отношения «многие ко многим» на самом деле довольно редки, и обычно это помогает вводить промежуточный объект для моделирования самого отношения. Это окажется неоценимым, если (когда!) Появятся случаи использования, требующие захвата свойств относительно отношения (например, является ли отношение «ребенок/родитель» естественным, суррогатным, усыновителем и т. Д.).

Итак, помимо объектов Person, Parent и Child, которые вы уже идентифицировали, давайте представим объект под названием ParentChildRelationship. Экземпляр ParentChildRelationship будет иметь ссылку только на одного родителя и одного ребенка, и оба родительского и дочернего классов будут содержать коллекцию этих объектов.

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

alt text

public abstract class Person 
{ 
} 

public class Parent : Person 
{ 
    private HashSet<ParentChildRelationship> _children = 
     new HashSet<ParentChildRelationship>(); 

    public virtual IEnumerable<ParentChildRelationship> Children 
    { 
     get { return this._children; } 
    } 

    public virtual void AddChild(Child child, RelationshipKind relationshipKind) 
    { 
     var relationship = new ParentChildRelationship() 
     { 
      Parent = this, 
      Child = child, 
      RelationshipKind = relationshipKind 
     }; 

     this._children.Add(relationship); 
     child.AddParent(relationship); 
    } 
} 

public class Child : Person 
{ 
    private HashSet<ParentChildRelationship> _parents = 
     new HashSet<ParentChildRelationship>(); 

    public virtual IEnumerable<ParentChildRelationship> Parents 
    { 
     get { return this._parents; } 
    } 

    internal virtual void AddParent(ParentChildRelationship relationship) 
    { 
     this._parents.Add(relationship); 
    } 
} 

public class ParentChildRelationship 
{ 
    public virtual Parent Parent { get; protected internal set; } 

    public virtual Child Child { get; protected internal set; } 

    public virtual RelationshipKind RelationshipKind { get; set; } 
} 

public enum RelationshipKind 
{ 
    Unknown, 
    Natural, 
    Adoptive, 
    Surrogate, 
    StepParent 
} 
+0

Изображение отсутствует ... Не могли бы вы разместить изображение на imgur вместо ссылки на ссылку Dropbox? – Sometowngeek

1

Если вы можете ограничить направление ассоциации только в одну сторону, вы сэкономите массу неприятностей (но это не всегда возможно).

Односторонняя связь:

public class Parent : Person 
{ 
    public IEnumerable<Person> Children { get; } 
} 

Если вы хотите иметь связь идущее другое направление, а также, вы можете сделать это тоже:

Однако, теперь у вас есть круговой что вам нужно поддерживать, и, хотя это возможно, это не особенно продуктивно.

Вы можете поддерживать связь как односторонние отношения, позволяя детям вызывать события вместо того, чтобы явно ссылаться на их родителя.

1

Я предположил бы, что ребенок также может быть родителем вниз линии (если он повезет ... или не повезло, в зависимости от точки зрения), так что я бы что-то вроде:

IPerson 
{ 
    string Name {get; set;} 
    string LastName {get; set;} 
    // whatever else - such as sizeOfShoe, dob, etc 
} 

IHaveParents 
{ 
    // might wanna limit this to a fixed size 
    List<IPerson> Parents {get; set;} 
} 

IHaveChildren 
{ 
    List<IPerson> Children {get; set;} 
} 

IHaveSpouse 
{ 
    IPerson Spouse {get; set;} 
} 

public class DudeWithParentsAndChildren : IPerson, IHaveParents, IHaveChildren, IHaveSpouse 
{  
    public void AskMoneyToParents(){throw new Exception("Implement me!");} 
    public void SlapChildren(){} 
    private void CheatOnSpouse(){} 
    // some other stuff that such a dude can do i.e. GoBowling 
} 

И вы можете легко распространить это так, как вам нравится, когда появятся новые требования (верьте мне, что они будут).

Update: Так что в вашем случае, если вы хотите ребенка, чтобы иметь родителей и наоборот вы могли бы сделать что-то вроде:

public class Child : IPerson, IHaveParents 
{  
    public void AskMoneyToParents(){throw new Exception("Implement me!");} 
} 

public class Parent : IPerson, IHaveChildren, IHaveSpouse 
{  
    public void SlapChildren(){} 
    private void CheatOnSpouse(){} 
    // some other stuff that such a dude can do i.e. GoBowling 
} 

Таким образом, если вы хотите иметь IHaveFriends интерфейс, который вы можете (что в основном заставляет исполнителя реализовать список IPersons как свойство «Друзья»). Если вам это не нужно, не делайте этого, но тот факт, что вы можете легко сделать это, просто добавив интерфейс, все остальное останется таким же, как у вас есть довольно приличная расширяемая модель (не обязательно лучшая, вы знаете что я имею в виду).

+0

Acctually в моем примере ребенок никогда не станет родителем, она выбрасывается из системы задолго до этого :-) – Zooking

+0

круто - но это всего лишь пример, сообщение, которое я пытаюсь чтобы понять, что я все равно создам классы Child и Parent, которые будут реализовывать соответственно те IHaveParents и IHaveChildren (кроме IPerson) для расширяемости. – JohnIdol

+0

Хорошо, но в моем примере можно было бы просто иметь интерфейс IHaveChildOrParent? Я не говорю, что было бы неплохо просто подумать, можно ли разделить эти два, и если есть какие-то преимущества. – Zooking

1

Как указал ДжонИдол, ребенок, каким-то образом, может стать родителем. Другими словами, НЕ делайте подкласс класса Parent и Child Person.

class Person 
{ 
    readonly List<Person> _children = new List<Person>(), 
         _parents = new List<Person>(); 

    public IEnumerable<Person> Children 
    { 
     get { return _children.AsReadOnly(); } 
    } 

    public IEnumerable<Person> Parents 
    { 
     get { return _parents.AsReadOnly(); } 
    } 

    public void AddChild(Person child) 
    { 
     _children.Add(child); 
     child._parents.Add(this); 
    } 

    public void AddParent(Person parent) 
    { 
     _parents.Add(parent); 
     parent._children.Add(this); 
    } 

    /* And so on... */ 
} 
+0

Если вы решите ввести конкретные классы для родителей и детей, это действительно зависит от характера разрабатываемой системы. Если это генеалогическое/семейное дерево, тогда я согласен, что вам нужен только конкретный класс Person. Но если вы разрабатываете программное обеспечение для отслеживания платежей по уходу за ребенком/пособием, то, вероятно, потребуются конкретные классы для Родительского и Ребенка, так как эти сущности будут иметь совершенно разные атрибуты, а тот факт, что ребенок может в конечном итоге стать самим родителем, не имеют отношения к системе. –

2
public class Person 
{ 
    Person Parent { get;set; } 
    IList<Person> Children { get;set; } 
} 

Родитель может быть пустым, если вы не знаете родителя. Дети могут быть пустыми или пустыми, если у вас нет детей. Поскольку каждый ребенок - Личность, у него может быть Родитель или его собственные дети.

Эта конструкция сама по себе прекрасна, пока вы не представите более подробные сценарии использования, касающиеся того, как она будет использоваться или сохраняется.

Смежные вопросы