Estoy desarrollando un modelo de objetos que tiene muchas clases diferentes de padres / hijos. Cada objeto hijo tiene una referencia a su objeto padre. Puedo pensar (y he intentado) varias formas de inicializar la referencia principal, pero encuentro inconvenientes significativos para cada enfoque. Dados los enfoques que se describen a continuación, cuál es el mejor ... o qué es aún mejor.
No voy a asegurarme de que el siguiente código se compila, así que intente ver mi intención si el código no es sintácticamente correcto.
Tenga en cuenta que algunos de mis constructores de clases secundarias toman parámetros (que no sean los primarios) aunque no siempre muestre ninguno.
La persona que llama es responsable de establecer el padre y agregar al mismo padre.
class Child { public Child(Parent parent) {Parent=parent;} public Parent Parent {get; private set;} } class Parent { // singleton child public Child Child {get; set;} //children private List<Child> _children = new List<Child>(); public List<Child> Children { get {return _children;} } }
Desventaja: establecer el padre es un proceso de dos pasos para el consumidor.
var child = new Child(parent); parent.Children.Add(child);
Desventaja: propenso a errores. La persona que llama puede agregar un niño a un padre diferente al que se usó para inicializar el niño.
var child = new Child(parent1); parent2.Children.Add(child);
Parent verifica que la persona que llama agrega child a parent para la cual se inicializó.
class Child { public Child(Parent parent) {Parent = parent;} public Parent Parent {get; private set;} } class Parent { // singleton child private Child _child; public Child Child { get {return _child;} set { if (value.Parent != this) throw new Exception(); _child=value; } } //children private List<Child> _children = new List<Child>(); public ReadOnlyCollection<Child> Children { get {return _children;} } public void AddChild(Child child) { if (child.Parent != this) throw new Exception(); _children.Add(child); } }
Desventaja: la persona que llama aún tiene un proceso de dos pasos para configurar a los padres.
Desventaja: comprobación de tiempo de ejecución: reduce el rendimiento y agrega código a cada agregado / configurador.
El padre establece la referencia del padre del niño (a sí mismo) cuando el niño se agrega / asigna a un padre. El setter primario es interno.
class Child { public Parent Parent {get; internal set;} } class Parent { // singleton child private Child _child; public Child Child { get {return _child;} set { value.Parent = this; _child = value; } } //children private List<Child> _children = new List<Child>(); public ReadOnlyCollection<Child> Children { get {return _children;} } public void AddChild(Child child) { child.Parent = this; _children.Add(child); } }
Desventaja: el hijo se crea sin una referencia principal. A veces, la inicialización / validación requiere el padre, lo que significa que se debe realizar alguna inicialización / validación en el setter primario del niño. El código puede complicarse. Sería mucho más fácil implementar el elemento secundario si siempre tuviera su referencia principal.
Principal expone métodos de adición de fábrica para que un hijo siempre tenga una referencia principal. El niño ctor es interno. El setter principal es privado.
class Child { internal Child(Parent parent, init-params) {Parent = parent;} public Parent Parent {get; private set;} } class Parent { // singleton child public Child Child {get; private set;} public void CreateChild(init-params) { var child = new Child(this, init-params); Child = value; } //children private List<Child> _children = new List<Child>(); public ReadOnlyCollection<Child> Children { get {return _children;} } public Child AddChild(init-params) { var child = new Child(this, init-params); _children.Add(child); return child; } }
Desventaja: no se puede usar la sintaxis de inicialización como
new Child(){prop = value}
. En cambio tiene que hacer:var c = parent.AddChild(); c.prop = value;
Desventaja: tiene que duplicar los parámetros del constructor hijo en los métodos add-factory.
Desventaja: no se puede usar un establecedor de propiedades para un hijo único. Parece lamentable que necesite un método para establecer el valor pero proporcionar acceso de lectura a través de un captador de propiedades. Es desigual.
El niño se agrega al padre al que se hace referencia en su constructor. El niño ctor es público. No hay acceso de agregar público desde el padre.
//singleton class Child{ public Child(ParentWithChild parent) { Parent = parent; Parent.Child = this; } public ParentWithChild Parent {get; private set;} } class ParentWithChild { public Child Child {get; internal set;} } //children class Child { public Child(ParentWithChildren parent) { Parent = parent; Parent._children.Add(this); } public ParentWithChildren Parent {get; private set;} } class ParentWithChildren { internal List<Child> _children = new List<Child>(); public ReadOnlyCollection<Child> Children { get {return _children;} } }
Desventaja: la sintaxis de llamadas no es excelente. Normalmente uno llama a un
add
método en el padre en lugar de simplemente crear un objeto como este:var parent = new ParentWithChildren(); new Child(parent); //adds child to parent new Child(parent); new Child(parent);
Y establece una propiedad en lugar de simplemente crear un objeto como este:
var parent = new ParentWithChild(); new Child(parent); // sets parent.Child
...
Acabo de enterarme de que SE no permite algunas preguntas subjetivas y claramente esta es una pregunta subjetiva. Pero, tal vez es una buena pregunta subjetiva.