Sto sviluppando un modello a oggetti che ha molte classi genitore / figlio diverse. Ogni oggetto figlio ha un riferimento al suo oggetto padre. Mi viene in mente (e ho provato) diversi modi per inizializzare il riferimento principale, ma trovo significativi svantaggi per ogni approccio. Dati gli approcci descritti di seguito quale è il migliore ... o cosa è ancora meglio.
Non mi assicurerò che il codice riportato di seguito venga compilato, quindi prova a vedere la mia intenzione se il codice non è sintatticamente corretto.
Nota che alcuni dei costruttori della mia classe figlio accettano parametri (diversi dai genitori) anche se non ne mostro sempre nessuno.
Il chiamante è responsabile dell'impostazione del genitore e dell'aggiunta allo stesso genitore.
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;} } }
Unico inconveniente: l' impostazione del genitore è un processo in due fasi per il consumatore.
var child = new Child(parent); parent.Children.Add(child);
Unico inconveniente: soggetto a errori. Il chiamante può aggiungere un figlio a un genitore diverso da quello utilizzato per inizializzare il figlio.
var child = new Child(parent1); parent2.Children.Add(child);
Parent verifica che il chiamante aggiunga child al parent per il quale è stato inizializzato.
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); } }
Unico inconveniente: il chiamante ha ancora un processo in due passaggi per impostare il genitore.
Unico inconveniente: controllo del runtime : riduce le prestazioni e aggiunge il codice a ogni add / setter.
Il genitore imposta il riferimento del genitore del figlio (su se stesso) quando il figlio viene aggiunto / assegnato a un genitore. Il setter principale è 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); } }
Unico inconveniente: il figlio viene creato senza un riferimento principale. A volte l'inizializzazione / convalida richiede il genitore, il che significa che l'inizializzazione / convalida deve essere eseguita nel setter genitore del bambino. Il codice può diventare complicato. Sarebbe molto più semplice implementare il bambino se avesse sempre avuto il suo riferimento genitore.
Parent espone metodi di aggiunta di fabbrica in modo che un figlio abbia sempre un riferimento padre. Il ctor del bambino è interno. Il setter parent è privato.
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; } }
Unico inconveniente: non è possibile utilizzare la sintassi di inizializzazione come
new Child(){prop = value}
. Invece devi fare:var c = parent.AddChild(); c.prop = value;
Svantaggio: duplicare i parametri del costruttore figlio nei metodi add-factory.
Unico inconveniente: non è possibile utilizzare un setter proprietà per un figlio singleton. Sembra zoppo che ho bisogno di un metodo per impostare il valore ma fornire l'accesso in lettura tramite un getter di proprietà. È sbilenco.
Il figlio si aggiunge al genitore a cui fa riferimento il suo costruttore. Child ctor è pubblico. Nessun accesso pubblico aggiuntivo da parte del genitore.
//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;} } }
Unico inconveniente: chiamare la sintassi non è eccezionale. Normalmente si chiama un
add
metodo sul genitore invece di creare un oggetto come questo:var parent = new ParentWithChildren(); new Child(parent); //adds child to parent new Child(parent); new Child(parent);
E imposta una proprietà anziché semplicemente creare un oggetto come questo:
var parent = new ParentWithChild(); new Child(parent); // sets parent.Child
...
Ho appena imparato che SE non consente alcune domande soggettive e chiaramente questa è una domanda soggettiva. Ma forse è una buona domanda soggettiva.