2016-03-17 3 views
0

Хорошо, я боролся с этой проблемой, которую я испытываю.Шаблон посетителя и логические операции

Я создал древовидную структуру с логическими узлами, например. И, Или, Равно, Между. Я не хочу, чтобы у этих узлов было что-то большее, чем метод accept для посетителя, потому что будет больше посетителей с различными реализациями.

Так, например, я посещаю и. Он сначала посещает себя, поэтому я знаю, что имею дело с a, и затем он посещает левый и правый узлы. Который может быть любым из ранее упомянутых узлов.

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

"и (Equals()/или (Equals()/Between())"

Но так как у меня нет никакого способа узнать, когда был посещен Чайлдс это будет коснуться, как это.

«И() Equals()/Или() Equals()/Между()

Любые предложения о том, как преодолеть это про я проблема?

+0

Проверка завершения работы с дочерними узлами? Что вы имеете в виду? Вы не закончили в конце вызова '' .accept() '' '? Вы делаете это асинхронно? –

ответ

0

Спасибо за советы jgauffin! Но у меня есть что-то меньшее, работая так, как я этого хочу.

Начал работать с дженериками.

public abstract class Node 
{ 
    public abstract T Accept<T>(IVisitor<T> visitor); 
} 

public interface IVisitor<T> 
{ 
    T Visit(And element); 
    T Visit(Or element); 
    T Visit(Equals element); 
} 

Так что мой посетитель может осуществить это без какого-либо беспокойства объектов, как это:

public string Visit(Or element) 
{ 
    return "Or(" + element.Left.Accept(this) + "," + element.Right.Accept(this) + ")"; 
} 

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

+0

Добро пожаловать в переполнение стека. Если вам понравился ответ от @jgauffin, вы должны проголосовать за него и/или принять его. – Fuhrmanator

0

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

Что-то вроде этого:

public class VisitContext 
{ 
    // Visited node 
    public VisitedType Visited { get; set; } 

    // Visited node is the left child node 
    public bool IsLeftNode { get; set; } 

    // Visited node is the right child node 
    public bool IsRightNode { get; set; } 
} 

Так посетитель получил этот контракт:

public interface IVisitor 
{ 
    public void Visit(VisitContext context); 
} 

Теперь вам просто нужно пройти через него. Ключ состоит в том, чтобы иметь защищенную перегрузку, которая сообщает детям, какой тип они есть. Обратите внимание на рекурсивные вызовы.

public class VisitedType 
{ 
    public void Accept(IVisitor visitor) 
    { 
     var context = new VisitContext{ Visited = this }; 
     visitor.Visit(context); 

     _leftNode.AcceptAsLeft(context); 
     _rightNode.AcceptAsRight(context); 
    } 

    protected void AcceptAsLeft(VisitContext context) 
    { 
     context.IsLeftNode=true; 
     context.IsRightNode=false; 
     visitor.Visit(context); 

     _leftNode.AcceptAsLeft(context); 
     _rightNode.AcceptAsRight(context); 
    } 

    protected void AcceptAsRight(VisitContext context) 
    { 
     context.IsLeftNode=false; 
     context.IsRightNode=true; 
     visitor.Visit(context); 

     _leftNode.AcceptAsLeft(context); 
     _rightNode.AcceptAsRight(context); 
    } 
} 

Теперь вы знаете, когда вы идете влево и вправо на дереве, но вы все еще не знаете, где вы находитесь на дереве. Для этого давайте представим линейку/сухарики, которые отслеживают наш текущий путь.

public class VisitContext 
{ 
    public VisitedType Visited { get; set; } 
    public bool IsLeftNode { get; set; } 
    public bool IsRightNode { get; set; } 

    // this. 
    public LinkedList<VisitedType> Lineage { get; set; }  
} 

И обновить посещаемый тип:

public class VisitedType 
{ 
    public void Accept(IVisitor visitor) 
    { 
     var context = new VisitContext{ Visited = this, Lineage = new LinkedList<VisitedType>() }; 
     context.Lineage.AddLast(this); 
     visitor.Visit(context); 

     _leftNode.AcceptAsLeft(context); 
     _rightNode.AcceptAsRight(context); 
    } 

    protected void AcceptAsLeft(VisitContext context) 
    { 
     //add a bread crumb 
     context.Lineage.AddLast(this); 
     context.IsLeftNode=true; 
     context.IsRightNode=false; 
     visitor.Visit(context); 

     _leftNode.AcceptAsLeft(context); 
     _rightNode.AcceptAsRight(context); 

     //remove us when we've visited our children 
     context.Lineage.RemoveLast(); 
    } 

    protected void AcceptAsRight(VisitContext context) 
    { 
     //add a bread crumb 
     context.Lineage.AddLast(this); 
     context.IsLeftNode=false; 
     context.IsRightNode=true; 
     visitor.Visit(context); 

     _leftNode.AcceptAsLeft(context); 
     _rightNode.AcceptAsRight(context); 

     //remove us when we've visited our children 
     context.Lineage.RemoveLast(); 
    } 
} 

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

Смежные вопросы