Я розробляю об'єктну модель, яка має безліч різних класів батьків / дітей. Кожен дочірній об’єкт має посилання на його батьківський об'єкт. Я можу придумати (і спробував) декілька способів ініціалізації батьківського посилання, але я знаходжу суттєві недоліки кожного підходу. З огляду на підходи, описані нижче, що найкраще ... або що ще краще.
Я не збираюся переконуватись у наведеному нижче коді, тому, будь ласка, спробуйте побачити мій намір, якщо код не синтаксично правильний.
Зауважте, що деякі мої конструктори дочірнього класу приймають параметри (крім батьківських), хоча я не завжди показую.
Абонент несе відповідальність за встановлення батьків та додавання до цього самого батьків.
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;} } }
Знизу: встановлення батьків - це двоетапний процес для споживача.
var child = new Child(parent); parent.Children.Add(child);
Нижня: схильна до помилок. Абонент може додати дитину до іншого батька, ніж це було використано для ініціалізації дитини.
var child = new Child(parent1); parent2.Children.Add(child);
Батько перевіряє, що абонент додає дитину до батьків, для яких було ініційовано.
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); } }
Знизу: абонент все ще має двоетапний процес встановлення батьків.
Нижня сторона: перевірка часу виконання - знижує продуктивність та додає код до кожного додавання / налаштування.
Батько встановлює батьківське посилання дитини (на себе), коли дитина додається / призначається батькові. Батьківський сеттер внутрішній.
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); } }
Нижня сторона: дитина створюється без батьківських довідок. Іноді для ініціалізації / валідації потрібен батько, а значить, певна ініціалізація / перевірка повинна бути виконана у батьківському наборі дитини. Код може ускладнитися. Було б набагато простіше реалізувати дитину, якби вона завжди мала посилання на батьків.
Батько виставляє фабричні методи додавання, щоб дитина завжди мала посилання на батьків. Дитячий ctor є внутрішнім. Батьківський сеттер приватний.
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; } }
Нижня частина: Неможливо використовувати синтаксис ініціалізації, такий як
new Child(){prop = value}
. Натомість робити:var c = parent.AddChild(); c.prop = value;
Нижня сторона: доведеться дублювати параметри дочірнього конструктора у методах додаткової фабрики.
Нижня сторона: Неможливо використовувати налаштування властивостей для одинокої дитини. Здається, кульгавим, що мені потрібен метод встановити значення, але забезпечити доступ для читання через геть властивості. Це однобічний.
Child додає себе до батьків, на які посилається у своєму конструкторі. Дитячий ctor є загальнодоступним. Немає загальнодоступного доступу з батьків.
//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;} } }
Знизу: виклик синтаксису не є великим. Зазвичай виклик
add
методу на батьківському, а не просто створення такого об'єкта:var parent = new ParentWithChildren(); new Child(parent); //adds child to parent new Child(parent); new Child(parent);
І встановлює властивість, а не просто створювати такий об’єкт:
var parent = new ParentWithChild(); new Child(parent); // sets parent.Child
...
Щойно я дізнався, що SE не допускає якихось суб'єктивних питань, і явно це суб'єктивне питання. Але, можливо, це гарне суб'єктивне питання.