2010-03-06 5 views
9

Tengo una clase Persona y dos clases heredadas llamadas Padre e hijo. Un padre puede tener n hijo (s) y un hijo puede tener n padre (s).Cómo hacer una referencia cruzada de objetos en las clases

¿Cuál es la mejor manera en OOD para crear una referencia entre un padre y un niño.

¿Debo crear una lista en cada clase que haga referencia al padre/hijo conectado o hay una forma mejor?

Respuesta

11

Una gran pregunta.Las relaciones puras de muchos a muchos son en realidad bastante raras, y generalmente ayuda introducir un objeto intermedio para modelar la relación en sí. Esto será invaluable si (¡cuando!) Surgen casos de uso que requieren la captura de propiedades con respecto a la relación (por ejemplo, si la relación niño/padre es natural, sustituta, adoptiva, etc.).

Por lo tanto, además de las entidades Person, Parent y Child que ya ha identificado, vamos a presentar un objeto llamado ParentChildRelationship. Una instancia de ParentChildRelationship tendrá una referencia exactamente a un padre y un hijo, y tanto las clases principales como las secundarias tendrán una colección de estas entidades.

Es una buena idea identificar los casos de uso que tiene para trabajar con estas entidades y agregar los métodos de ayuda adecuados para mantener las referencias entre objetos. En el siguiente ejemplo, he elegido agregar un método público AddChild al padre.

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

Falta la imagen ... ¿Puede publicar la imagen en imgur en lugar de hacer una referencia al enlace de Dropbox? – Sometowngeek

1

Si puede limitar la dirección de la asociación para ir solo de una manera, se ahorrará muchos problemas (pero esto no siempre es posible).

una vía de relación:

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

Si usted quiere tener la asociación va la otra dirección, así, se puede hacer lo mismo:

Sin embargo, ahora tienen una circular referencia que necesita mantener, y si bien es posible, no es particularmente productivo.

A menudo puede mantener la asociación como una relación unidireccional al permitir a los niños plantear eventos en lugar de hacer referencia explícita a su elemento primario.

1

Me imagino que un niño también puede ser un padre en la línea (si tiene suerte ... o mala, dependiendo de los puntos de vista) así que me gustaría ir con algo como:

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 
} 

Y podría extenderlo fácilmente de la manera que desee cuando surjan nuevos requisitos (créanme que lo harán).

actualización: Así, en su caso si sólo se quiere que un niño tiene padres y al revés que haría algo como:

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 
} 

De esta manera si usted quiere tener un IHaveFriends interfaz que puede (lo que básicamente obliga al implementador a exponer una lista de IPersons como una propiedad llamada Friends). Si no lo necesitas, no lo hagas, pero el hecho de que puedes hacerlo fácilmente simplemente agregando una interfaz y todo lo demás permanece igual significa que tienes un modelo extensible bastante decente (no necesariamente el mejor, ya sabes lo que quiero decir).

+0

Acctually en mi ejemplo el niño nunca llegará a ser un padre, que es expulsado del sistema mucho antes de que :-) – Zooking

+0

fresco - pero eso es sólo un ejemplo, el mensaje que estoy tratando para transmitir es que todavía crearía clases para niños y padres para que implementen, respectivamente, los IHaveParents y IHaveChildren (que no sean IPerson) para la extensibilidad. – JohnIdol

+0

Ok, pero en mi ejemplo, ¿sería posible tener la interfaz IHaveChildOrParent? No estoy diciendo que sería bueno solo preguntarme si sería posible compartir los dos y si hay alguna ventaja. – Zooking

1

Como señaló JohnIdol, un niño en algún momento puede convertirse en padre. En otras palabras, NO haga subclases de Persona de Padre e Hijo.

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

Si elige introducir clases concretas para padres e hijos realmente depende de la naturaleza del sistema que se está desarrollando. Si se trata de software de genealogía/árbol genealógico, entonces estoy de acuerdo en que solo desearía una clase de Persona concreta. Pero si está desarrollando un software para rastrear el mantenimiento infantil/pagos de beneficios, entonces se requerirían clases concretas para padres e hijos, ya que estas entidades tendrían atributos muy diferentes, y el hecho de que un niño podría convertirse en padre ser irrelevante para el sistema. –

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

los padres puede ser nulo cuando no se conoce la matriz. Los niños pueden ser nulos o estar vacíos cuando no tiene hijos. Como cada niño es una Persona, puede tener un Padre o sus propios hijos.

Este diseño, por sí solo, está bien hasta que proporcione casos de uso más detallados sobre cómo se usará o persistirá.

Cuestiones relacionadas