Как работать с узлами? - PullRequest
       1

Как работать с узлами?

0 голосов
/ 12 января 2011

Я создаю дерево для разбора текста, поэтому я создал несколько узлов:

abstract class Node { }

class TextNode : Node
{
    public readonly string Text;
    public TextNode(string text)
    {
        Text = text;
    }
    public TextNode(char ch)
    {
        Text = ch.ToString();
    }
}

class VariableNode : Node
{
    public readonly string Name;
    public VariableNode(string name)
    {
        Name = name;
    }
}

Я собираюсь добавить некоторые "ветвящиеся" узлы довольно скоро (узлы, которые содержат другие узлы).

Мне интересно, как лучше их обработать. Прямо сейчас у меня есть код, который выглядит так:

foreach (var item in nodes)
{
    if (item is TextNode)
        sb.Append(((TextNode)item).Text);
    if (item is VariableNode)
        sb.Append(dict[((VariableNode)item).Name]);
}

Что кажется немного неуклюжим и будет только ухудшаться, когда я добавлю к нему.

Должен ли я

  1. Попробуйте как-то инкапсулировать логику в базовый класс Node, чтобы я мог просто использовать DoStuff() функцию, не заботясь о приведении типов?
  2. Должен ли я добавить какое-нибудь перечисление для каждого типа узла, чтобы я мог использовать переключатель вместо цикла foreach?
  3. Что-то еще?

Нужно дать вам немного больше контекста. В приведенном выше примере узлам переменных необходим доступ к dict для визуализации.

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


Чтобы уточнить # 1

// in class Template
public string Render(Dictionary<string, object> dict)
{
    var sb = new StringBuilder();

    foreach (var item in nodes)
        sb.Append(item.Render(dict));

    return sb.ToString();
}

interface INode {
    string Render(Dictionary<string, object> dict);
}

class TextNode : INode
{
    private string _text;

    public TextNode(string text)
    {
        _text = text;
    }
    public TextNode(char ch)
    {
        _text = ch.ToString();
    }

    public string Render(Dictionary<string, object> dict)
    {
        return _text;
    }
}

class VariableNode : INode
{
    private string _name;

    // TODO: filters...

    public VariableNode(string name)
    {
        _name = name;
    }

    public string Render(Dictionary<string, object> dict)
    {
        return dict[_name].ToString();
    }
}

Думаю, это тоже не такое уж плохое решение.


Решения на данный момент:

  1. Используйте перечисление и переключатель
  2. Создайте словарь типов / действий, чтобы сделать переключатель немного чище
  3. Полиморфизм (х2)
  4. Шаблон посетителей (еще не читал)
  5. Использовать ANTLR - хотя ANTLR будет строить дерево, а затем эта проблема снова будет применяться

Ответы [ 5 ]

2 голосов
/ 12 января 2011

Переместите ваш foreach (элемент var в узлах) в метод (например, BuildAll) в классе абстрактных узлов, создайте абстрактный / виртуальный метод Build (в абстрактном классе Node) и вызовите метод Build () в BuildAll ипусть выводит строку ...

Примерно так:

    public abstract string Build();

    public string BuildAll() {
      var output = new StringBuilder();
      foreach(var node in nodes) {
        output.append(node.Build()); 
      }
      return output.toString();
    }

и переопределяет ее внутри каждой фактической реализации узла ... так что TextNode будет иметь ...

public override string Build() {
  return ... extract whatever you want from TextNode;
}

В этом случае вашему базовому классу не нужно знать точную реализацию его потомка, поэтому он не нарушает принцип Open Close.

2 голосов
/ 12 января 2011

Если у вас будет много разных типов узлов с разным поведением, я бы, конечно, подумал об использовании базового класса Node, а затем о реализации специализированного класса узлов для каждого типа, который реализует определенное поведение.

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

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

2 голосов
/ 12 января 2011

Вместо этого можно использовать interface и определить свойство enum, например, для. NodeType Type, где Type - это enum со значениями TextNode, VariableNode. Хотя и здесь вам нужно будет привести узел, чтобы получить значение. Вы можете попробовать добавить свойство с именем Value, которое возвращает Text или Name в зависимости от NodeType. Если dict доступен из VariableNode, Value может вернуть точное значение, необходимое для вашего метода.

В целом, я думаю, что интерфейсы удовлетворяют вашим требованиям лучше, чем абстрактный класс.

1 голос
/ 12 января 2011

Вы можете создать Dictionary<Type,Action<T>> - ключи - это типы узлов, а Action<T> делегаты - то, что вы делаете с каждым.

Вы можете просто сделать:

Dictionary[typeof(variable)];

Чтобы выполнить правильное действие для каждого типа.

1 голос
/ 12 января 2011

Полиморфизм именно то, что вы хотите. Примерно так:

class Node
{
   public virtual string ToDisplayText(params object[] parameters)
   {
      return string.Empty;
   }
}
class TextNode : Node
{
   public override string ToDisplayText(params object[] parameters)
   {
      return this.Text;
   }
}
class VariableNode : Node
{
   public override string ToDisplayText(params object[] parameters)
   {
      //check parameters
      var dict = (Dictionary<string,string>)parameters[0];
      return dict[this.Name];
   }
}

Так что вы можете:

foreach(var node in nodes)
{
   sb.Append(node.ToDisplayText(dict));
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...