Допустим, есть у меня дерево, его вершины — объекты классов NodeClass1, NodeClass2 ... и RootNodeClass. То есть, например, класс RootNodeClass может создать у себя детей типа 1 и 2, в свою очередь NodeClass1 может иметь детей типа 1, 3, 4, ну и так далее в самых разных комбинациях.
И допустим, один из классов, NodeClassX, нуждается в данных, которые есть в корневой вершине, но его "предкам" эти данные не нужны.
Как правильно их туда передать?
Следует ли тащить эти данные через цепочку конструкторов? Тогда получится, что большинство классов будут тащить как бы лишний параметр в конструкторе.
Update:
В каждой вершине есть свойство Parent, однако оно заполняется после конструирования, тогда как данные из корня требуются уже в конструкторе класса NodeClassX.
Может быть, сделать так, чтобы конструкторы всех типов вершин обязательно требовали ссылку на предка?
Здравствуйте, dmitry_npi, Вы писали:
_>Добрый день.
_>Допустим, есть у меня дерево, его вершины — объекты классов NodeClass1, NodeClass2 ... и RootNodeClass. То есть, например, класс RootNodeClass может создать у себя детей типа 1 и 2, в свою очередь NodeClass1 может иметь детей типа 1, 3, 4, ну и так далее в самых разных комбинациях.
_>И допустим, один из классов, NodeClassX, нуждается в данных, которые есть в корневой вершине, но его "предкам" эти данные не нужны. _>Как правильно их туда передать? _>Следует ли тащить эти данные через цепочку конструкторов? Тогда получится, что большинство классов будут тащить как бы лишний параметр в конструкторе.
А если передавать ссылку на RootNodeClass? Тогда всяким NodeClass1, NodeClass2 нет нужды знать определение корневого класса, а NodeClassX уже может (зная определение рут-класса) получать нужные ему данные, вызывая какой-то его метод. Правда, все равно понадобится, конечно, передавать через конструкторы.
Ну или вовсе передавать в конструкторе какой-то прокси объект, который содержит ссылку на RootNodeClass и знает как у него узнать данные. Но полностью скрывает другие его интерфейсы, выдавая какой-нить только один метод GetDataFromRoot, который де факто и будет общаться со ссылкой на рут-класс. Тогда по крайней мере нет необходимости дочерние класса знать про рут. Его полностью будет скрывать прокси-объект, разрешая только нужное и не более.
Здравствуйте, Carc, Вы писали:
C>А если передавать ссылку на RootNodeClass? Тогда всяким NodeClass1, NodeClass2 нет нужды знать определение корневого класса, а NodeClassX уже может (зная определение рут-класса) получать нужные ему данные, вызывая какой-то его метод. Правда, все равно понадобится, конечно, передавать через конструкторы.
C>Ну или вовсе передавать в конструкторе какой-то прокси объект, который содержит ссылку на RootNodeClass и знает как у него узнать данные. Но полностью скрывает другие его интерфейсы, выдавая какой-нить только один метод GetDataFromRoot, который де факто и будет общаться со ссылкой на рут-класс. Тогда по крайней мере нет необходимости дочерние класса знать про рут. Его полностью будет скрывать прокси-объект, разрешая только нужное и не более.
Спасибо. В конце концов заставил все конструкторы принимать ссылку на родителя. Таким образом можно добраться до корня из любого места.
1. Я думаю любому ребенку следует знать о его родители, это классическая реализация xml документа. Соответсетвенно документ всегда достижим. Конечно в зависимости от необходимости можно оптимизировать доступ.
2. Посмотрите на Annotations в XObject. Суть в том что любой узел может хранить дополнительные данные (определенного типа). Это если нужно именно любые. Если они заранее известны — то проперти конечно.
Это общее решение которое годится для люьых деревьев. Доказано практикой — в дотнете расширяемся так — в браузере расширяем объект по необходимости.
Просто из описания не совсем ясно что нужно. Такой общий механизм избавил бы от деталей. Как пример в xml документе — позиция в файле хранится именно так — и только если парсеру об этом сказать — поэтому в нормальных случаях не платим за это.
Здравствуйте, dmitry_npi, Вы писали:
_>Спасибо. В конце концов заставил все конструкторы принимать ссылку на родителя. Таким образом можно добраться до корня из любого места.
Здравствуйте, dmitry_npi, Вы писали:
_>И допустим, один из классов, NodeClassX, нуждается в данных, которые есть в корневой вершине, но его "предкам" эти данные не нужны. _>Как правильно их туда передать? _>Следует ли тащить эти данные через цепочку конструкторов? Тогда получится, что большинство классов будут тащить как бы лишний параметр в конструкторе.
А вы точно не пытаетесь запихнуть элементы внешнего алгоритма внутрь дерева, роль которого — контейнер?
Здравствуйте, dmitry_npi, Вы писали: _>Update: _>В каждой вершине есть свойство Parent, однако оно заполняется после конструирования, тогда как данные из корня требуются уже в конструкторе класса NodeClassX. _>Может быть, сделать так, чтобы конструкторы всех типов вершин обязательно требовали ссылку на предка?
Если речь про шарп, то классика выглядит так:
1.
OwnedCollection
// код древнючий, как минимум nameof() надо использовать. И ошибки при работе с коллекцией ловитьpublic abstract class OwnedCollection<TItem, TOwner> : Collection<TItem> // or KeyedCollection<TKey, TItem>where TItem : class
where TOwner : class
{
private readonly TOwner m_Owner;
private readonly Func<TItem, TOwner> m_GetOwner;
private readonly Action<TItem, TOwner> m_SetOwner;
protected OwnedCollection(
TOwner owner,
Func<TItem, TOwner> getOwner,
Action<TItem, TOwner> setOwner)
{
Code.NotNull(owner, "owner");
Code.NotNull(getOwner, "getOwner");
Code.NotNull(setOwner, "setOwner");
this.m_Owner = owner;
this.m_GetOwner = getOwner;
this.m_SetOwner = setOwner;
}
protected override void ClearItems()
{
foreach (var item in Items)
{
m_SetOwner(item, null);
}
base.ClearItems();
}
protected override void InsertItem(int index, TItem item)
{
Code.AssertState(m_GetOwner(item) == null, "Item is mapped to another owner.");
base.InsertItem(index, item);
m_SetOwner(item, m_Owner);
}
protected override void RemoveItem(int index)
{
var item = this[index];
base.RemoveItem(index);
m_SetOwner(item, null);
}
protected override void SetItem(int index, TItem item)
{
Code.AssertState(m_GetOwner(item) == null, "Item is mapped to another owner.");
m_SetOwner(this[index], null);
base.SetItem(index, item);
m_SetOwner(item, m_Owner);
}
}
var schema = new Tag("SOMETAG")
{
Attributes =
{
{ "version", "0.1" }
},
Tags =
{
new Tag("Subtag")
{
new Attribute("SomeFieldAttribute", n => GetSomeField(n)),
new Attribute("Field2Attribute", n => GetField2(n)),
new Tag("Subtag2")
{
...
),
new Tag("Subtag3")
{
...
}
},
GetMoreTags()
}
};
4. Делаем Extension-метод IEnumerable<Node> Ancestors(), который ч/з yield return перебирает всех родителей + IEnumerable<TNode> Ancestors<TNode>(), который возвращает только родителей заданного типа. Позволяет вытащить свойства из узлов выше по дереву.
5. Всё, что зависит от родителей прячется за Lazy<TValue>, после смены родителя Lazy сбрасываются.
6. Если надо проверить дерево в сборе — добавляем метод Init(), наследники в нём заполняют нужные свойства, ну и запрещаем изменение дерева после вызова этого метода.