Tworzę model obiektowy, który ma wiele różnych klas nadrzędnych / podrzędnych. Każdy obiekt podrzędny ma odniesienie do swojego obiektu nadrzędnego. Mogę wymyślić (i próbowałem) kilka sposobów na zainicjowanie referencji nadrzędnej, ale uważam, że każde podejście ma znaczące wady. Biorąc pod uwagę opisane poniżej podejścia, które są najlepsze ... lub jeszcze lepsze.
Nie zamierzam się upewnić, że poniższy kod się skompiluje, więc spróbuj zobaczyć mój zamiar, jeśli kod nie jest poprawny pod względem składniowym.
Zauważ, że niektóre z moich konstruktorów klas potomnych pobierają parametry (inne niż nadrzędne), chociaż nie zawsze je wyświetlam.
Dzwoniący jest odpowiedzialny za ustawienie rodzica i dodanie do tego samego rodzica.
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;} } }
Wada: ustawienie rodzica jest procesem dwuetapowym dla konsumenta.
var child = new Child(parent); parent.Children.Add(child);
Wada: podatność na błędy. Dzwoniący może dodać dziecko do innego rodzica niż ten, którego użyto do zainicjowania dziecka.
var child = new Child(parent1); parent2.Children.Add(child);
Rodzic sprawdza, czy dzwoniący dodaje dziecko do rodzica, dla którego zostało zainicjowane.
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); } }
Wada: dzwoniący nadal ma dwuetapowy proces ustawiania rodzica.
Wada: sprawdzanie w czasie wykonywania - zmniejsza wydajność i dodaje kod do każdego dodającego / ustawiającego.
Rodzic ustawia odwołanie do rodzica dziecka (do siebie), gdy dziecko jest dodawane / przypisywane do rodzica. Setter nadrzędny jest wewnętrzny.
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); } }
Wada: dziecko jest tworzone bez odniesienia rodzica. Czasami inicjalizacja / walidacja wymaga rodzica, co oznacza, że pewna inicjalizacja / walidacja musi zostać wykonana w seterze rodzica dziecka. Kod może się skomplikować. O wiele łatwiej byłoby zaimplementować dziecko, gdyby zawsze miało ono odniesienie do rodzica.
Rodzic udostępnia fabryczne metody dodawania, dzięki czemu dziecko zawsze ma odniesienie rodzica. Dziecko jest wewnętrzne. Seter nadrzędny jest prywatny.
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; } }
Wada: nie można użyć składni inicjującej, takiej jak
new Child(){prop = value}
. Zamiast tego musisz zrobić:var c = parent.AddChild(); c.prop = value;
Wada: trzeba powielić parametry konstruktora potomnego w metodach add-factory.
Wada: nie można użyć narzędzia do ustawiania właściwości dla pojedynczego dziecka. Wydaje się kiepskie, że potrzebuję metody ustawiania wartości, ale zapewniam dostęp do odczytu za pośrednictwem modułu pobierania właściwości. Jest krzywy.
Dziecko dodaje się do rodzica, do którego odwołuje się jego konstruktor. Dziecko jest publiczne. Brak publicznego dodawania dostępu od rodzica.
//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;} } }
Wada: wywołanie składni nie jest świetne. Zwykle wywołuje się
add
metodę nadrzędną zamiast po prostu utworzyć taki obiekt:var parent = new ParentWithChildren(); new Child(parent); //adds child to parent new Child(parent); new Child(parent);
I ustawia właściwość zamiast tworzyć taki obiekt:
var parent = new ParentWithChild(); new Child(parent); // sets parent.Child
...
Właśnie dowiedziałem się, że SE nie pozwala na pewne subiektywne pytania i wyraźnie jest to pytanie subiektywne. Ale może to dobre pytanie subiektywne.