Я разрабатываю объектную модель, которая имеет много разных родительских / дочерних классов. Каждый дочерний объект имеет ссылку на свой родительский объект. Я могу придумать (и попробовал) несколько способов инициализации родительской ссылки, но я нахожу существенные недостатки каждого подхода. Учитывая подходы, описанные ниже, что лучше ... или что еще лучше.
Я не собираюсь удостовериться, что код ниже компилируется, поэтому, пожалуйста, постарайтесь увидеть мое намерение, если код не является синтаксически правильным.
Обратите внимание, что некоторые из моих конструкторов дочерних классов принимают параметры (кроме родительских), хотя я не всегда показываю их.
Абонент отвечает за установку родителя и добавление к тому же родителю.
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 не допускает некоторые субъективные вопросы, и ясно, что это субъективный вопрос. Но, может быть, это хороший субъективный вопрос.