Sto sviluppando un modello a oggetti che ha molte diverse classi genitore / figlio. Ogni oggetto figlio ha un riferimento al suo oggetto genitore. Posso pensare (e ho provato) diversi modi per inizializzare il riferimento genitore, ma trovo degli svantaggi significativi per ciascun approccio. Considerati gli approcci descritti qui di seguito che è il migliore ... o ciò che è ancora meglio.
Non ho intenzione di assicurarmi che il codice qui sotto sia compilato, quindi prova a vedere la mia intenzione se il codice non è sintatticamente corretto.
Si noti che alcuni dei miei costruttori di classi figlio accettano parametri (diversi dai genitori) anche se non ne mostro sempre.
-
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;} } }
Il lato negativo: l'impostazione del genitore è un processo in due fasi per il consumatore.
var child = new Child(parent); parent.Children.Add(child);
Lato negativo: soggetto a errori. Il chiamante può aggiungere un figlio a un genitore diverso da quello utilizzato per inizializzare il bambino.
var child = new Child(parent1); parent2.Children.Add(child);
-
Il genitore verifica che il chiamante aggiunga un figlio al genitore 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); } }
Lato negativo: il chiamante ha ancora una procedura in due passaggi per l'impostazione genitore.
Lato negativo: controllo runtime - riduce le prestazioni e aggiunge codice a ogni add / setter.
-
Il genitore imposta il riferimento genitore del figlio (a se stesso) quando il bambino viene aggiunto / assegnato a un genitore. Il setter genitore è 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); } }
Lato negativo: il figlio viene creato senza un riferimento genitore. A volte l'inizializzazione / convalida richiede il genitore, il che significa che deve essere eseguita un'inizializzazione / convalida nel setter del genitore del bambino. Il codice può diventare complicato. Sarebbe molto più semplice implementare il bambino se avesse sempre il suo riferimento genitore.
-
Il genitore espone i metodi di aggiunta di fabbrica in modo che un bambino abbia sempre un riferimento genitore. Il bambino è interno. Il setter genitore è 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; } }
Lato negativo: Impossibile utilizzare la sintassi di inizializzazione come
new Child(){prop = value}
. Invece devi fare:var c = parent.AddChild(); c.prop = value;
Lato negativo: Devi duplicare i parametri del costruttore figlio nei metodi add-factory.
Lato negativo: Impossibile usare un setter di proprietà per un figlio singleton. Sembra che io abbia bisogno di un metodo per impostare il valore, ma fornire l'accesso in lettura tramite un getter di proprietà. È sbilenco.
-
Child si aggiunge al genitore referenziato nel suo costruttore. Il bambino è pubblico. Nessun accesso pubblico 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;} } }
Lato negativo: la sintassi di chiamata non è eccezionale. Normalmente si chiama un metodo
add
sul genitore invece di creare semplicemente 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é creare semplicemente un oggetto come questo:
var parent = new ParentWithChild(); new Child(parent); // sets parent.Child
...
Ho appena saputo che SE non consente alcune domande soggettive e chiaramente questa è una domanda soggettiva. Ma forse è una buona domanda soggettiva.