Общее недвоичное дерево с разными типами на уровень - PullRequest
0 голосов
/ 27 июня 2019

Я пытаюсь создать универсальное дерево иерархии объектов, которое позволяет осуществлять поиск объектов по имени и идентификатору, а также обход.Базовый класс выглядит следующим образом:

public abstract class RuntimeObject
{
  public Guid Id {get;}
  public string Name {get;}
  public Type ObjectType {get;}

  protected RuntimeObject( string name )
  {
    Id = Guid.NewGuid();
    Name = name;
  }
}

Этот класс предназначен для наследования классами, которые являются частью иерархии.

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

// Top-level parent class
public abstract class RuntimeObject<TChild> : RuntimeObject
  where TChild : RuntimeObject<RuntimeObject<TChild>>
{
  public int ChildCount {get;}
  public TChild FirstChild {get;}
}

// Middle-level class
public abstract class RuntimeObject<TParent,TChild> : RuntimeObject
  where TParent : RuntimeObject<RuntimeObject<TParent,TChild>>
  where TChild : RuntimeObject<RuntimeObjecT<TParent,TChild>>
{
  public TParent Parent {get;}
  public RuntimeObject<TParent,TChild> NextSibling {get;}

  public int ChildCount {get;}
  public TChild FirstChild {get;}
}

// Leaf class
public abstract class RuntimeObject<TParent> : RuntimeObject
  where TParent : RuntimeObject<RuntimeObject<TParent>>
{
  public TParent Parent {get;}
  public RuntimeObject<TParent> NextSibling {get;}
}

Мало того, что это чтение с глазу на глаз, но это также не работает.

Цель : пусть A будет родительским элементом верхнего уровня, C будет листом, а B будет дочерним элементом A и будет иметь детей типа C. В идеале реализация иерархии должна выглядеть как-тов соответствии с:

public class A : RuntimeObject<B>
{ ... }

public class B : RuntimeObject<A,C>
{ ... }

public class C : RuntimeObject<B>
{ ... }

Эта реализация не работает, но идея стоит: я хотел бы создать общую систему для реализации обходной иерархии объектов и хотел бы избегать необходимости, чтобы конкретные классы определяли поля Parent или Child, если они не нужны.

Существует ли шаблон проектирования для этого?У кого-нибудь есть предложения по его структурированию?

1 Ответ

1 голос
/ 27 июня 2019

Если вы используете один класс для представления узла с дочерними элементами или без него, вы можете определить, является ли узел листовым, посмотрев, есть ли у него дочерние элементы. Читайте свойства. Если дочерние элементы нулевые (или пустые, в зависимости от того, как вы их пишете), тогда это лист. Это просто.

Если вы определяете отдельный класс для представления листового узла, у узла теперь есть два способа быть листовым узлом. Это может быть тип «листовой узел», или это может быть узел ветви без дочерних узлов. Это немного неловко, и если узел не является неизменным или не может быть создан с нулевыми дочерними элементами, а дочерние элементы не могут быть установлены на нулевые значения, это означает, что вам всегда придется выполнять обе проверки. Это листовой узел? Сначала проверьте его тип. Если это не тип конечного узла, также проверьте, есть ли у него дочерние элементы. Тебе придется сделать то же самое, пройдя по нему. Сначала убедитесь, что это не тип листа и также , посмотрите, есть ли у него дети.

Если узлы дерева неизменны, это может привести к путанице при построении дерева. Вы не можете создать узел, не зная сначала, есть ли у него дочерние элементы, и если у него есть дочерние элементы, вы должны предоставить их. И то же самое относится и к тем детям. (Вероятно, есть сценарий для этого, но ваши узлы не являются неизменяемыми, поэтому я предполагаю, что это не то, что вы хотели. И даже в этом сценарии определение отдельного класса все равно сделает обход дерева более эффективным.)

Это также означает определение новых типов, которые ничего не делают, но представляют существующий тип в другом состоянии. Другими словами, узел без дочерних элементов уже представлен "нормальным" классом узла ветви. Легко осмотреть узел и посмотреть, есть ли у него дети. Тот факт, что с типом «листовой узел» вам придется проверять как тип, так и свойства, и что обе проверки по сути скажут вам одно и то же, показывает, почему тип будет избыточным. Это буквально просто еще один способ обозначить то же самое.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...