BesucherPattern, neue Fähigkeiten zur Laufzeit?!

gott_ad

Grünschnabel
Hi Forumfreunde,

mein Problem ist folgendes (ich mach mal ein einfacheres Beispeil daraus):
Stellt euch vor ich habe ein Programm mit dem ich einen Baum aufbaue (ein Wurzelknoten, mehrere Kindknoten die Blätter oder Knoten sein können... ihr kennt das ja). Dieser Baum (er ist eine Instanz der Klasse "Baum") soll jetzt traversiert (durchwandert) werden. Da gibt es ja zum Beispiel 3 klassische Möglichkeiten (ich glaub die waren so): Postorder (erst rechts langgehen) Inorder (erst die mitte entlang) oder Preorder (erst links langgehen).
Wie auch immer, ich will der Baumklasse jetzt ein Traversierungselement zuweisen (wahrscheinlich ne Unterklasse der Klasse "Traverse") und dann sagen "los, durchwandere meinen baum" und je nachdem welchen ich mit der Baumklasse verknüft habe, wird Pre-/Post-/Inorder ausgeführt.

Meiner Meinung nach schreit das ja förmlich nach dem Besucher Muster (bin jetzt bei den Patterns nicht ganz so bewandert), oder? Es soll mit dem besucher-Pattern aber auch möglich sein, dass sich Besucher zur Laufzeit ändern oder neue hinzukommen. Hat da jemand ne Idee?
Wäre ja cool wenn ich ein Traversierungselement mitzuliefere und später einfach ne weitere DLL in einen unterordner zu kopiere und er fragt mich dann, welche Traversierungsart ich nehmen will.

Gruß gott_ad
 

Thomas Darimont

Erfahrenes Mitglied
Hallo!

Wie wär's denn, wenn du die Traversierungstrategie dynamisch zur Laufzeit per Reflection setzt? Weiterhin könntest du auch eine Art Callback-Modus (vielleicht per Delegates) implementieren, über den dann die Aktion, welche für jeden Besuchten Knoten ausgeführt wird enthält.
Code:
  using System;
  using System.Reflection;
  
  namespace De.Tutorials
  {
  	/// <summary>
  	/// Zusammendfassende Beschreibung für Class1.
  	/// </summary>
  	class TreeWalkExample
  	{
  		/// <summary>
  		/// Der Haupteinstiegspunkt für die Anwendung.
  		/// </summary>
  		[STAThread]
  		static void Main(string[] args)
  		{
  			//Siehe:
  			//http://en.wikipedia.org/wiki/Post-order_traversal
  			TreeNode Root = new TreeNode(2);
  			Root.LeftChild = new TreeNode(7);
  			Root.LeftChild.LeftChild = new TreeNode(2);
  			Root.LeftChild.RightChild = new TreeNode(6);
  			Root.LeftChild.RightChild.LeftChild = new TreeNode(5);
 			Root.LeftChild.RightChild.RightChild = new TreeNode(11);
  
  			Root.RightChild = new TreeNode(5);
  			Root.RightChild.RightChild = new TreeNode(9);
  			Root.RightChild.RightChild.LeftChild = new TreeNode(4);
  
  			Console.WriteLine("PreOrderTraversal");
 		 ITraversalStrategy traversalStrategy = (ITraversalStrategy)Activator.CreateInstance(Type.GetType("De.Tutorials.PreOrderTraversalStrategy"));
  			traversalStrategy.Traverse(Root);
  
  			Console.WriteLine("InOrderTraversal");
 		 traversalStrategy = (ITraversalStrategy)Activator.CreateInstance(Type.GetType("De.Tutorials.InOrderTraversalStrategy"));
  			traversalStrategy.Traverse(Root);
  
  			Console.WriteLine("PostOrderTraversal");
 		 traversalStrategy = (ITraversalStrategy)Activator.CreateInstance(Type.GetType("De.Tutorials.PostOrderTraversalStrategy"));
  			traversalStrategy.Traverse(Root);
  
  		}
  	}
  	
  	class TreeNode
  	{
  		public object Data;
  		public TreeNode LeftChild;
  		public TreeNode RightChild;
  			
  		public TreeNode(object Data)
  		{
  			this.Data = Data;
  		}
  	}
  
  	interface ITraversalStrategy
  	{
  		void Traverse(TreeNode Node);
  	}
  
  	class PreOrderTraversalStrategy:ITraversalStrategy
  	{
  		public void Traverse(TreeNode Node)
  		{
  			Console.WriteLine(Node.Data);
  			if(Node.LeftChild != null)
  			{
  				Traverse(Node.LeftChild);
  			}
  			if(Node.RightChild != null)
  			{
  				Traverse(Node.RightChild);
  			}
  		}
  	}
  
  	class InOrderTraversalStrategy:ITraversalStrategy
  	{
  		public void Traverse(TreeNode Node)
  		{
  			if(Node.LeftChild != null)
  			{
  				Traverse(Node.LeftChild);
  			}
  			Console.WriteLine(Node.Data);
  			if(Node.RightChild != null)
  			{
  				Traverse(Node.RightChild);
  			}
  		}
  	}
  
  	class PostOrderTraversalStrategy:ITraversalStrategy
  	{
  		public void Traverse(TreeNode Node)
  		{
  			if(Node.LeftChild != null)
  			{
  				Traverse(Node.LeftChild);
  			}
  			if(Node.RightChild != null)
  			{
  				Traverse(Node.RightChild);
  			}
  			Console.WriteLine(Node.Data);
  		}
  	}
  }

Gruss Tom
 

Thomas Darimont

Erfahrenes Mitglied
Hallo!

Weiterhin könntest du auch eine Art Callback-Modus (vielleicht per Delegates) implementieren, über den dann die Aktion, welche für jeden Besuchten Knoten ausgeführt wird enthält.
Das meinte ich übrigens ungefähr so:

C#:
  using System;
   using System.Reflection;
   
   namespace De.Tutorials
   {
   	/// <summary>
   	/// Zusammendfassende Beschreibung für Class1.
   	/// </summary>
   	class TreeWalkExample
   	{
   		/// <summary>
   		/// Der Haupteinstiegspunkt für die Anwendung.
   		/// </summary>
   		[STAThread]
   		static void Main(string[] args)
   		{
   			//Siehe:
   			//http://en.wikipedia.org/wiki/Post-order_traversal
   			TreeNode root = new TreeNode(2);
   			root.leftChild = new TreeNode(7);
   			root.leftChild.leftChild = new TreeNode(2);
   			root.leftChild.rightChild = new TreeNode(6);
   			root.leftChild.rightChild.leftChild = new TreeNode(5);
 			root.leftChild.rightChild.rightChild = new TreeNode(11);
   
   			root.rightChild = new TreeNode(5);
   			root.rightChild.rightChild = new TreeNode(9);
 			root.rightChild.rightChild.leftChild = new TreeNode(4);
   
 			MethodInfo methodInfo = typeof(TreeWalkExample).GetMethod("PrintDataForCurrentNode",new Type[]{typeof(TreeNode)});
 		 AbstractTraversalStrategy.ActionForCurrentNode actionForCurrentNode = (AbstractTraversalStrategy.ActionForCurrentNode)
 System.Delegate.CreateDelegate(typeof(AbstractTraversalStrategy.ActionForCurrentNode),methodInfo);
   
   			Console.WriteLine("PreOrderTraversal");
 		 AbstractTraversalStrategy traversalStrategy = (AbstractTraversalStrategy)Type.GetType("De.Tutorials.PreOrderTraversalStrategy").GetConstructor(new Type[]{typeof(AbstractTraversalStrategy.ActionForCurrentNode)}).Invoke(new Object[]{actionForCurrentNode});
   			traversalStrategy.Traverse(root);
   
   			Console.WriteLine("InOrderTraversal");
 		 traversalStrategy = (AbstractTraversalStrategy)Type.GetType("De.Tutorials.InOrderTraversalStrategy").GetConstructor(new Type[]{typeof(AbstractTraversalStrategy.ActionForCurrentNode)}).Invoke(new Object[]{actionForCurrentNode});
   			traversalStrategy.Traverse(root);
   
   			Console.WriteLine("PostOrderTraversal");
 		 traversalStrategy = (AbstractTraversalStrategy)Type.GetType("De.Tutorials.PostOrderTraversalStrategy").GetConstructor(new Type[]{typeof(AbstractTraversalStrategy.ActionForCurrentNode)}).Invoke(new Object[]{actionForCurrentNode});
   			traversalStrategy.Traverse(root);
   
   			Console.ReadLine();
   
   		}
   
   		public static void PrintDataForCurrentNode(TreeNode node)
   		{
   			Console.WriteLine(node.data);
   		}
   	}
   	
   	class TreeNode
   	{
   		public object data;
   		public TreeNode leftChild;
   		public TreeNode rightChild;
   			
   		public TreeNode(object data)
   		{
   			this.data = data;
   		}
   	}
   
   	abstract class AbstractTraversalStrategy
   	{	
   		public delegate void ActionForCurrentNode(TreeNode node);
   		public ActionForCurrentNode doActionForCurrentNode;
   
   		public AbstractTraversalStrategy(ActionForCurrentNode action)
   		{
   			this.doActionForCurrentNode = action;
   		}
   
   		public abstract void Traverse(TreeNode node);
   	}
   
   
   
   	class PreOrderTraversalStrategy:AbstractTraversalStrategy
   	{
   		
   		public PreOrderTraversalStrategy(ActionForCurrentNode action):base(action)
   		{
   
   		}
   
   		public override void Traverse(TreeNode node)
   		{
   			doActionForCurrentNode(node);
   			if(node.leftChild != null)
   			{
   				Traverse(node.leftChild);
   			}
   			if(node.rightChild != null)
   			{
   				Traverse(node.rightChild);
   			}
   		}
   	}
   
   	class InOrderTraversalStrategy:AbstractTraversalStrategy
   	{
   
   		public     InOrderTraversalStrategy(ActionForCurrentNode action):base(action)
   		{
   
   		}
   
   		public override void Traverse(TreeNode node)
   		{
   			if(node.leftChild != null)
   			{
   				Traverse(node.leftChild);
   			}
   			doActionForCurrentNode(node);
   			if(node.rightChild != null)
   			{
   				Traverse(node.rightChild);
   			}
   		}
   	}
   
   	class PostOrderTraversalStrategy:AbstractTraversalStrategy
   	{
   
   		public PostOrderTraversalStrategy(ActionForCurrentNode action):base(action)
   		{
   		}
   
   		public override void Traverse(TreeNode node)
   		{
   			if(node.leftChild != null)
   			{
   				Traverse(node.leftChild);
   			}
   			if(node.rightChild != null)
   			{
   				Traverse(node.rightChild);
   			}
   			doActionForCurrentNode(node);
   		}
   	}
   }

Gruss Tom
 

gott_ad

Grünschnabel
Als erstes riesen Dank für deine super ausführliche Antwort mit den hervorragenden Beispielen

Mit Delegates kenn ich mich wenig aus, aber auch mal sehr schön eine Reflection Variante davon zu sehen. Es ist natürlich praktisch die Funktion zum Auswerten nur einmal zu schreiben und alle nutzen diese. Da die Verarbeitungsfunktionen aber wahrscheinlich auch unterschiedliche sachen machen können sollen, werde ich wohl die erste Variante benutzen ;-)

Im grunde brauch ich mich mit dem BeuscherPattern nicht rumzuschlagen, wenn ich einfach ein Interface und Reflection nutze! Dann kann ich auch zur Laufzeit neue Funktionalitäten einpflegen.

Danke!
gott_ad