2010-03-06 25 views
9

Ho una classe Person e due classi ereditate denominate Parent and Child. Un genitore può avere n bambini (s) e un bambino può avere n genitore (s).Come riferimenti incrociati agli oggetti nelle classi

Qual è il modo migliore in OOD per creare un riferimento tra un genitore e un bambino.

Devo creare un elenco in ogni classe che fa riferimento al genitore/figlio collegato o esiste un modo migliore?

risposta

11

Ottima domanda.Pure le relazioni molti-a-molti sono in realtà piuttosto rare e di solito aiuta a introdurre un oggetto intermedio per modellare la relazione stessa. Ciò si rivelerà inestimabile se (quando!) Emergono casi d'uso che richiedono l'acquisizione di proprietà riguardanti la relazione (ad esempio se la relazione bambino/genitore è naturale, surrogata, adottiva, ecc.).

Quindi, oltre alle entità Persona, Genitore e Figlio che hai già identificato, introduciamo un oggetto chiamato ParentChildRelationship. Un'istanza di ParentChildRelationship avrà un riferimento a esattamente un genitore e un figlio secondario e entrambe le classi padre e figlio terranno una raccolta di tali entità.

È una buona idea quindi identificare i casi d'uso che si hanno per lavorare con queste entità e aggiungere metodi di supporto appropriati per mantenere i riferimenti tra oggetti. Nell'esempio seguente ho appena scelto di aggiungere un metodo AddChild pubblico al genitore.

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

L'immagine è mancante ... Puoi postare l'immagine su imgur invece di fare un riferimento al link Dropbox? – Sometowngeek

1

Se è possibile limitare la direzione dell'associazione per andare solo a in un modo, risparmierete un sacco di problemi (ma questo non è sempre possibile).

senso unico rapporto:

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

Se si vuole avere l'associazione andando nella direzione opposta, così, si può farlo anche:

Tuttavia, ora avete una circolare riferimento che è necessario mantenere, e mentre è possibile, non è particolarmente produttivo.

È spesso possibile mantenere l'associazione come una relazione unidirezionale lasciando che i bambini aumentino gli eventi invece di fare esplicito riferimento al genitore.

1

mi immagino che un bambino può anche essere un genitore su tutta la linea (se ottiene fortunato ... o sfortunato, a seconda del punto di vista), quindi vorrei andare con qualcosa di simile:

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 
} 

E potresti facilmente estenderlo come preferisci quando arrivano nuovi requisiti (credimi, lo faranno).

Aggiornamento: Quindi nel tuo caso, se si desidera solo un bambino ad avere genitori e viceversa faresti qualcosa di simile:

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 
} 

questo modo, se si vuole avere un IHaveFriends l'interfaccia che puoi (che sostanzialmente costringe l'implementatore a esporre un elenco di IPerson come una proprietà chiamata Amici). Se non ne hai bisogno, non farlo, ma il fatto che puoi facilmente farlo aggiungendo un'interfaccia e tutto il resto rimane lo stesso significa che hai un modello estensibile abbastanza decente (non necessariamente il migliore, sai ciò che intendo).

+0

Acctually nel mio esempio il bambino non diventerà mai un genitore, si è gettato fuori del sistema molto prima che :-) – Zooking

+0

fresco - ma questo è solo un esempio, il messaggio che sto cercando per attraversare è che vorrei ancora creare classi Child e Parent facendole implementare rispettivamente quelli IHaveParents e IHaveChildren (diversi da IPerson) per l'estensibilità. – JohnIdol

+0

Ok, ma nel mio esempio sarebbe possibile avere solo l'interfaccia IHaveChildOrParent? Non sto dicendo che sarebbe bello chiedersi se sarebbe possibile condividere i due e se ci sono dei vantaggi. – Zooking

1

Come ha sottolineato JohnIdol, un bambino in qualche modo lungo la linea potrebbe diventare un genitore. In altre parole, NON creare sottoclassi padre-figlio della persona.

Parent
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

Sia che tu scelga di introdurre le classi di genitore e figlio in concreto dipende molto dalla natura del sistema in fase di sviluppo. Se si tratta di software genealogico/albero genealogico, sono d'accordo che vorresti solo una classe Persona concreta. Ma se stai sviluppando un software per tenere traccia dei pagamenti di mantenimento/indennità dei figli, è probabile che siano richieste classi concrete per Genitore e Bambino, poiché queste entità avrebbero attributi molto diversi e il fatto che un bambino potrebbe diventare un genitore stesso essere irrilevante per il sistema. –

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

può essere nullo se non si conosce il genitore. I bambini possono essere nulli o vuoti quando non ci sono bambini. Poiché ogni bambino è una persona, può avere un genitore oi propri figli.

Questo progetto, di per sé, va bene finché non si forniscono scenari di caso d'uso più dettagliati su come verrà usato o mantenuto.

Problemi correlati