Обработка нулевого родителя T всего объекта T - PullRequest
0 голосов
/ 25 мая 2011

BaseUnit> Блок> ContainerUnit

  • BaseUnit является базовым классом.
  • Модуль добавляет свойство ContainerUnit с именем Parent.
  • ContainerUnit добавляет свойство List с именем Children.

Итак, все типы юнитов (включая ContainerUnit) должны иметь родителя, который является ContainerUnit. Типы ContainerUnit могут иметь дочерние элементы, которые являются типами ContainerUnit или просто типами Unit.

Таким образом, вы можете иметь коробку предметов, некоторые из которых являются коробками предметов.

Я хочу иметь главный контейнерный узел, который обрабатывается как родительский элемент самого высокого уровня для всех типов юнитов. Но это сделало бы его родительское свойство нулевым. То есть я хочу сказать "кто твой папа?" к чему-либо, не зная о его положении в иерархии, но затем, если я спрашиваю (скажем, в цикле), кто является родительским контейнером главного контейнера, он обрабатывается изящно.

Я ищу подходы, которые другие предприняли, чтобы решить эту проблему. Я искал это, мне просто не повезло с моими запросами.

Ответы [ 3 ]

7 голосов
/ 25 мая 2011

Традиционный способ сделать так, чтобы внешний контейнер «юниверса» возвращал значение «ноль» для своего контейнера.Преимущество в том, что это легко.Недостатком является то, что вы не знаете, что прошли через край вселенной, пока не стало слишком поздно, чтобы вернуться.Как вы сказали в комментарии: использование «нуля» в качестве флага является слабым.

Два других решения, которые я видел, используются:

1) Объект юниверса - это собственный контейнер.Это имеет то преимущество, что ничто не является нулевым;у него есть недостаток, заключающийся в том, что при ходьбе по контейнерной цепи легко зайти в бесконечный цикл, и это не интуитивно понятно;вселенная на самом деле не содержит себя.По сути, вы используете равенство как флаг вместо обнуляемости как флаг;это тоже кажется слабым.

2) Объект юниверса выдает исключение, когда вы запрашиваете контейнер.Это вынуждает вызывающего вместо того, чтобы проверять наличие нулевого контейнера, вместо этого проверять, "Вы - вся вселенная?"прежде чем просить контейнер.То есть остановитесь, когда вы доберетесь до вершины, вместо того, чтобы остановиться, когда вы выйдете за верх.Это действительно хорошее решение, потому что оно заставляет людей писать защитный код.Вы не можете просто попросить контейнер, если не знаете, что он есть.Конечно, это требует, чтобы вызывающая сторона каким-то образом могла идентифицировать объект юниверса без проверки его родителя.Вам нужен "Я вся вселенная?"метод, или известный одноэлементный объект для сравнения, или какой-то другой механизм для определения того, какой контейнер является самым верхним.

Третий подход - отрицать предпосылку вопроса;Можно ли построить ваш тип данных таким образом, чтобы контейнер не был известен, или чтобы его знание было сведено к минимуму?

Например, в компиляторе, конечно, у нас много цепочек "контейнеров"ходить, и мы сигнализируем глобальное пространство имен, имея его содержащий символ быть нулевым (и тем, что он является известным одноэлементным объектом.) Но в большинстве случаев нам не нужно проверять, является ли родительский объект нулевым, потому чтовместо этого я пишу код, который создает абстракцию поверх него:

static IEnumerable<Container> AllContainers(this Thing thing)
{
    if (thing == null) yield break;
    Container current = thing.Container;
    while(current != null) 
    {
        yield return current;
        current = current.Container;
    }
}

Отлично.Теперь, когда у меня есть этот вспомогательный метод, мне больше не нужно проверять свойство Container какой-либо вещи.Если я хочу знать, «есть ли контейнер этой вещи, который содержит эту другую вещь?»тогда я могу сказать:

var query = from container in oneThing.AllContainers()
            where container.Contains(otherThing)
            select container;
bool result = query.Any();

Используйте мощь LINQ для перемещения деталей механистической реализации, таких как «как определить, когда я на вершине?»в высокоуровневые вспомогательные методы.Затем напишите свою логику на уровне «бизнеса», а не на уровне «механизма».

2 голосов
/ 25 мая 2011

Лучший способ, которым я могу придумать, это просто обработать ваш нулевой случай.То есть, когда вы смотрите на ContainerUnit и пытаетесь получить его Parent, вы просто добавляете проверку на ноль.

И пример может быть:

//Get all the Master Units from batch XYZ
var masterUnits = batches.Where(b => b.BatchId = XYZ)
                  .Single().Units.Where(u=> u.Parent == null);

Другойпример

//Get only units which have a parent [batch is an already initialized variable]
var childUnits = batch.Units.Where(u=> u.Parent != null);
1 голос
/ 25 мая 2011

Я думаю, что свойство Parent возвращает null, когда это самый высокий объект в иерархии, имеет наибольшее значение и является наиболее изящным решением.

...