# Composite, Design Pattern, printTree



## guni (21. Jan 2008)

Hi,

bin neu in Java und habe aus gg. Anlass heraus probiert, ein Composite-Design-Pattern auszuprogrammieren ...

dabei habe ich 
* eine abstrakte Klasse _Tree_ von der sich 
* 2 Klassen ableiten: _Folder_ und _File_

das ganze soll darauf hinauslaufen, dass ich eine hierachische Baumstruktur aufbauen will.
Jetzt will ich in der Basisklasse eine Methode printTree schreiben, die von gegebenem Element weg den Verzeichnisbaum ausgibt.
Aber irgendwie bring ich das Ganze nicht so auf die Reihe, wie ich es mir erhoffen würde.
Das Problem: ich könnte das Ganze theoretisch rekursiv lösen, aber die tatsächlichen Elemente des Verzeichnisbaumes werden im Echteinsatz mal aus der Datenbank ausgelesen und da werd ich warscheinlich mehrere 10.000 Elemente haben ... das könnte mir rekursiv irgendwann einen Stack-Overflow geben ...

also, könnt ihr mir ein bisschen auf die Sprünge helfen wie ich eine prtinTree mit Schleifen ausprogrammieren könnte?
mein Strukturmuster verwendet einen ListIterator um den Tree aufzubauen.

lg, guni


----------



## SlaterB (21. Jan 2008)

10.000 Elemente ist erstmal nicht schlimm,
bei einem binären Baum (jeder Knoten hat zwei Nachfolger) wäre das ja offensichtlich nur eine Tiefe von 14, 
bei noch breiteren Bäumen siehts noch besser aus,

oder hast du einen verrückten Baum mit tausenden Elementen untereinander?

die Alternative zu Rekursion ist jedenfalls eine Liste aller noch offenen Knoten,
aus der nimmst du jeweils einen raus, holst dir dessen Kindern und packst diese auch in die Liste, mehr ist das kaum


----------



## guni (21. Jan 2008)

hmm ... tiefe von 14 - ok.
bis zu welcher tiefe würdest du rekursionen denn emfehlen?
wenn ich eine Tiefe von 20 hätte, könnt ich ja schon über eine Million Elemente darstellen 
(bei einem binären Baum !!!)

aber wer sagt, dass mein Baum binär ist? Wenn es so wäre, dann würde ich da was falsch verstehen weil ein Ordner in einem Verzeichnis (Knoten) kann ja mehr als 2 Unterverzeichnisse aufnehmen (mehr als 2 Nachfolger!)

hast du das gemeint, wo du von einem breiteren Baum gesprochen hast?
Dass dei Elemente nicht so tief verschachtelt werden sondern eher in der Ebene bleiben?
Die Rekursion hat in der *Ebene* keine Auswirkungen auf Speicherbelastung, oder? Nur in der *Ebenentiefe*! 
Oder versteh ich da wieder was falsch??

lg, guni

PS.: 
* wie würde eine Rekursion für sowas ansatzweise aussehen? ich hab da immer einen stack-overflow-error gehabt!
* wie kann ich die ebenentiefe ermitteln? (= meine Einrückung)
* gibt es in Java eine Funktion, die mir eine bestimmte anzahl von Leerzeichen vor einen String schreibt oder muss ich mir sowas selber schreiben?
* das parent-element kann ich schon mit übergeben, oder ;-)


----------



## SlaterB (21. Jan 2008)

> bis zu welcher tiefe würdest du rekursionen denn emfehlen? 

unter 1000

> aber wer sagt, dass mein Baum binär ist?

das ist nur der Worst-Case unter den normalen Bäumen, deiner ist wohl breiter ja, dann ist es noch besser

> Die Rekursion hat in der Ebene keine Auswirkungen auf Speicherbelastung, oder? 

wenn man sie richtig baut, ja

> wie würde eine Rekursion für sowas ansatzweise aussehen?

was willst du übrhaupt bauen?
ganz normal wäre z.B.

String beschreibung = "";
beschreibung += (Beschreibung für aktuellen Knoten);
for (alle Kinder) {
beschreibung += (rekursiver Aufruf für Kind);
}

bei 1000 Elementen allerdings besser nicht Strings addieren,
sondern alles in einem StringBuilder einfügen, der als Parameter herumgereicht wird

> wie kann ich die ebenentiefe ermitteln?

auch per Rekursion

int tiefe = 1 + übergebene Tiefe;
int maxTiefe = tiefe;
for (alle Kinder) {
maxTiefe = max(rekursiver Aufruf für Kind mit tiefe als Parameter);
}

> gibt es in Java eine Funktion, die mir eine bestimmte anzahl von Leerzeichen vor einen String schreibt oder muss ich mir sowas selber schreiben? 

denke nicht

> das parent-element kann ich schon mit übergeben, oder 

mach was du für richtig hälst,
aber: jeder Parameter füllt den StackTrace etwas mehr und verrngert die mögliche Rekursionstiefe  :bae:


----------



## guni (21. Jan 2008)

> was willst du übrhaupt bauen?



ich denke es ist an der Zeit, mal ein bisschen code zu posten 

also: hier meine basisklasse:


```
package structural.composite;

import java.util.LinkedList;
import java.util.ListIterator;

@SuppressWarnings("unchecked")
public abstract class Tree {
	LinkedList TreeList; 
	Tree parent;
	String name;
	
	public abstract boolean add(Tree itemToAdd);
	public abstract boolean remove(Tree itemToRemove);
	public abstract ListIterator createListIterator();
	
	public void setParent(Tree parentIn) {
		parent = parentIn;
	}	
	public Tree getParent() {
		return parent;
	}
	public void setName(String nameIn) {
		name = nameIn;
	}
	
	public String getName() {
		return name;
	}
	
	public void printTree(Tree parent){
		// DAS WILL ICH BAUEN!!! 
		// KEINE AHNUNG WIE DAS GEHEN SOLL! MIR FEHLT VOLL DER ANSATZ!
	}

}
```

dazu gibt es eine abgeleitete Klasse Folder:


```
package structural.composite;

import java.util.LinkedList;
import java.util.ListIterator;

@SuppressWarnings("unchecked")
public class Folder extends Tree {

	public Folder(String nameIn){
		TreeList = new LinkedList();
		this.setName(nameIn);
	}
	
	@Override
	public boolean add(Tree itemToAdd) {
		itemToAdd.setParent(this);
		return TreeList.add(itemToAdd);
	}

	@Override
	public ListIterator createListIterator() {
		ListIterator listIterator = TreeList.listIterator();
		return listIterator;
	}

	@Override
	public boolean remove(Tree itemToRemove) {
		ListIterator listIterator = this.createListIterator();
		Tree tempTree;
		while (listIterator.hasNext()) {
			tempTree = (Tree)listIterator.next();
			if (tempTree == itemToRemove) {
               listIterator.remove();
               return true;
           }
       }
       return false;
	}

}
```

... und dann noch eine Klasse Link (= File)


```
package structural.composite;

import java.util.ListIterator;

@SuppressWarnings("unchecked")
public class Link extends Tree {
	String LinksTo;
	
	public Link(String nameIn) {
		this.setName(nameIn);
	}
	
	public boolean addReference(String linkIn){
		this.LinksTo = linkIn;
		return true;
	}

	@Override
	public boolean add(Tree itemToAdd) {
		return false;
	}

	@Override
	public ListIterator createListIterator() {
		return null;
	}

	@Override
	public boolean remove(Tree itemToRemove) {
		return false;
	} 

}
```

mein Main Programm könnte dann (in der 1. Entwicklungsstufe circa das hier:


```
package mainapp;

import structural.composite.*;
@SuppressWarnings("unchecked")
public class CompanyHierachy {

	public static void main(String[] args){
		
		Folder root = new Folder("lsalarchiv");
		Folder Firma1 = new Folder("Firma1");
		Folder Firma2 = new Folder("Firma2");
		Folder AbtA = new Folder("Abteilung A");
		Folder AbtB = new Folder("Abteilung B");
		Folder AbtC = new Folder("Abteilung C");
		Folder AbtD = new Folder("Abteilung D");
		Folder AbtE = new Folder("Abteilung E");
		Folder AbtF = new Folder("Abteilung F");
		
		root.add(Firma1);
		root.add(Firma2);
		
		Firma1.add(AbtA);
		Firma1.add(AbtB);
		Firma1.add(AbtC);
		Firma1.add(AbtD);
		Firma2.add(AbtE);
		Firma2.add(AbtF);
		
		
		root.printTree(root);
	}

}
```

das problem:
ich häng an der rekursion.

mir fehlt voll der ansatz!!!
konzeptionell gesehen würde es ja auch reichen, wenn sie in der basisklasse noch gar nicht aufscheint, sondern 'nur' auf ordnerebene, oder?! eine datei kann ja eh keine children haben! andererseits sollen dateien genauso mit ausgegeben werden!

könnt ihr mir helfen?

lg, guni


----------



## guni (21. Jan 2008)

weiß irgendwer weiter?!
HILFE!


----------



## SlaterB (21. Jan 2008)

ich zumindest habe dir das Konzept erstellt,
wenn dir dazu nix einfällt, weiß ich nicht, was ich weiter erzählen soll,

kennst du dich denn mit einfacheren Rekursionen aus?
z.B. Fakultät rekursiv berechnen, eine LinkedList rekursiv durchlaufen?


----------



## maki (21. Jan 2008)

Das Prblem mit rekursio ist, dass sie durch die Anzahl der Stackframes begrenzt wird, dann bekommst du deinen Fehler.
Als Alternative kommt iteration in Frage, am besten wenn man so etwas wie eine Stack-Collection verwendet.

Werd nachher mal Googeln, vielleicht findet sich ein Beispiel.


----------



## guni (21. Jan 2008)

@SlaterB:
fakultät ja, linkedList durchlaufen nein - aber genau darum geht es ja, oder?!?!
@maki:
würdest du eher schleifen empfehlen. ich denke nicht, dass ich jemals eine rekursionstiefe von 1000 überschreiten werde ...


----------



## guni (21. Jan 2008)

@SlaterB ...

mit dem DesignPattern habe ich ja praktisch eine LinkedList, die aus genau solchen Listen besteht ...


----------



## maki (21. Jan 2008)

> würdest du eher schleifen empfehlen. ich denke nicht, dass ich jemals eine rekursionstiefe von 1000 überschreiten werde ..


Dann kannst du dir die Iterative Lösung ersparen.

Hier mein Tipp:
Benutze das Visitor Pattern 

Erzeuge zwei interfaces,

1. Visitor

```
public interface Visitor{
	public boolean visit(Tree host);	
}
```

2. Visitable

```
public interface Visitable{
	public void accept(Visitor Visitor);
}
```

Lass deinen Tree das Visitable Interface implementieren, sollte in etwa so aussehen:

```
public abstract class Tree 
implements Visitable{
...
    public void accept(Visitor visitor) {
        if (nodeVisitor.visit(this) == true) {
            for (Tree child : TreeList )
                child.accept(visitor);
            }
        }
    }
...
}
```
Sobald der Visitor false zurückgibt, wird die rekursive Verarbeitung abgebrochen.

Solltest imho dafür sorgen, dass Tree als "Kinder" nur Trees enthalten kann, denn darum geht es beim Kompositium/Composite, d.h. Typisierte Listen, dann sparst du dir auch das ignorieren der unchecked Warnings.

Der Visitor hat nun die Logik, ein Beispiel:

```
public class ExampleVisitor 
implements Visitor {

        public boolean visit(Tree host) {
            // hier kommt deine Logik die was mit den einzelnen Trees macht

            // Abfrage, ob das maximumlevel schon erreicht ist
            if(currentLevel >= maxLevel) {
                return false;
            } else {
                return true;
            }

        }
}
```

Aufruf mit:

```
Tree myTree = new .....
Visitor myVisitor = new ExampleVisitor();
...
myTree.accept(myVisitor);
```
So in etwa 

Kannst beliebig viele Visitor erstellen und so Funktonalität hinzufügen, ohne die Klassen zu verändern, machst einfach weitere Visitorimplementierungen.
Wie du siehst funktonieren Pattern am besten im Verbund.


----------



## SlaterB (21. Jan 2008)

> aber genau darum geht es ja, oder?!?! 

hier könnte es schwieriger sein, weil du mehrere Richtungen verfolgen musst,

machen wir es noch einfacher,
du hast ein Objekt der Klasse A und eines der Klasse B,
B ist in A enthalten, in A hast du eine Ausgabeoperion die alles von A und B ausgeben soll, wie machst du das?


```
public class Test
{

    public static void main(String[] args)
        throws Exception
    {
        A a = new A();
        System.out.println(a); // Ausgabe "a, b"?
    }
}


class A
{
    String name = "a";
    B b = new B();

    public String toString()
    {
        return name;
    }
}


class B
{
    String name = "b";

    public String toString()
    {
        return name;
    }
}
```


----------



## jPat (21. Jan 2008)

Evtl. möchtest du es ja so haben:


```
public void printTree(Tree parent,String s){
	   for ( Object o : parent.TreeList){
		   if (o instanceof Folder) {
			   System.out.println(s+"(F) "+o);
			   ((Folder)o).printTree((Tree)o, s+"-");
		   }
		   if (o instanceof Link) {
			   System.out.println(s+"(L) "+o);
		   }	   
	   }
   }
   @Override
   public String toString() {
	   // TODO Auto-generated method stub
	   return name;
   }
```

[EDIT] nun auch mit eine Strukturierten Ausgabe. Aufruf: root.printTree(root,"");


----------



## guni (21. Jan 2008)

@slater:

1. ich versteh nicht ganz, was das Beispiel mit meinem Problem zu tun hat.

2. kenn ich mich zu wenig in Java aus um zu verstehen, wie das funktioniert, da meiner Meinung nach ein Fehler hätte auftreten müssen. Du schreibst 
	
	
	
	





```
System.out.println(a);
```
 _a_ ist ein Objekt! Es besteht aus Attributen und Methode(n) - woher aber weiß printf, was es ausgeben soll!

ich glaube, dass man diese Frage beantworten muss um zu wissen, wieso da was ausgegeben wird. 

abgesehen davon versteh ich nicht, wieso ich da etwas in mehrere richtungen verfolgen muss!
vielleicht habe ich mein problem falsch dargestellt:

printTree soll eine METHODE sein!
Nach oben hin interessiert mich der Baum gar nicht mehr. 
ich lege ein root-Objekt an und dann sag ich einfach

```
root.printTree();
```
wenn das ganze von einem Unterknoten aufgerufen wird, dann wird der Tree eben erst ab dem Unterknoten angezeigt!

die Funktion soll ungefähr das tun:

durchsuche alle Unterverzeichnisse/Datein und gib sie solange aus, bis du nix mehr findest.

meine 1. große Frage bei der Rekursion wäre: was ist die Abbruchbedingung???

In meinem obenstehenden Beispiel sollte die Fkt. folgendes ausgeben:

lsalarchiv
lsalarchiv/Firma1
lsalarchiv/Firma1/Abteilung A
lsalarchiv/Firma1/Abteilung B
lsalarchiv/Firma1/Abteilung C
lsalarchiv/Firma1/Abteilung D
lsalarchiv/Firma2
lsalarchiv/Firma2/Abteilung E
lsalarchiv/Firma2//Abteilung F

wie kann ich das lösen? ist das wirklich so schwer? ich dachte, ich bring es einfach nur deswegen nicht zsamm, weil ich
A) mich nicht soo gut mit Rekursionen auskenne
B) ich neu in Java bin

aber es wirkt, als wäre die Sache schon etwas komplexer, oder?!

lg, guni


----------



## jPat (21. Jan 2008)

Was heist hier komplexer  :lol: 


```
public void printTree(Tree parent,String s){
	   for ( Object o : parent.TreeList){
		   if (o instanceof Folder) {
			   System.out.println(s+"/"+o);
			   ((Folder)o).printTree((Tree)o, s+"/"+o);
		   }
		   if (o instanceof Link) {
			   System.out.println(s+"/"+o);
		   }	   
	   }
   }
   @Override
   public String toString() {
	   // TODO Auto-generated method stub
	   return name;
   }
```

Ohh, wie Komplex!


----------



## jPat (21. Jan 2008)

System.out.println(a) ruft halt die Methode a.toString() auf.

So ist das in Java.


----------



## SlaterB (21. Jan 2008)

> ich versteh nicht ganz, was das Beispiel mit meinem Problem zu tun hat. 

es lehrt, Rekursion zu erlernen (hoffe ich zumindest  )

> woher aber weiß printf, was es ausgeben soll! 

nun gut, das muss man einmal wissen: es wird bei Objekten deren toString()-Operation aufgerufen,

> versteh ich nicht, wieso ich da etwas in mehrere richtungen verfolgen muss!

wenn du nur weniger Richtungen siehst, umso besser,
ich will nur sagen, dass eine lineare Liste doch einfacher aufgebaut ist als ein verzweigter Baum,
dass in einer Liste die Rekursion deutlicher ist

> was ist die Abbruchbedingung??? 

wenn ein Knoten keine Kinder hat, dann die Operation nicht rekursiv für die nichtvorhandenen Kinder aufrufen 
in diesem Fall also trivial

> ist das wirklich so schwer?
> aber es wirkt, als wäre die Sache schon etwas komplexer, oder?! 

nein, ist machbar, aber für dich anscheinend schwer


----------



## guni (21. Jan 2008)

FREAK!!!!  :toll: 

just to get this straight:


```
for ( Object o : parent.TreeList)
```
damit durchlaufe ich die Liste des Objects, für die ich den Code aufrufe, oder?!
und damit habe ich dann auch immer eine Abbruchbedingung.

verstehe ich das richtig?!?!

danke jedenfalls!
das scheint zu funktionieren; d.h. ich kann mich auf die DB stürzen 

lg, guni


----------



## masta // thomas (21. Jan 2008)

Trotzdem wär ein Visitor die eleganteste Lösung


----------



## guni (21. Jan 2008)

weil?


----------



## jPat (21. Jan 2008)

Ja, das ist richtig.

man kann sie auch als for-each-Schleife sehen.

gruß
Patrick


----------



## guni (21. Jan 2008)

ok ... und welche vorteile habe ich jetzt, wenn ich statt einer simplen rekursion ein visitor-pattern ausprogrammiere?!

lg, guni


----------



## guni (21. Jan 2008)

okay ... nochmal zu meiner Frage:

ich habe eine Baumdarstellung, die ich als Composite gelöst habe.
Die Idee: 
eine abstrakte Klasse,
die ihre Methoden an 2 Subklassen (Ordner und Datei) vererbt.

der Vorteil: printTree ist eine Methode, die ich in der Basisklasse erstellen kann!
sie ist unabhängig von den Subklassen!

jetzt bekomme ich den Vorschlag, noch ein Visitor-Muster zu realisieren. 
Es hat den Vorteil, dass man einen Algorithmus vom Objekt trennen kann ...
also aus einer Objektmethode eine allgemeine Funktion machen kann.

Nur seh ich nicht den Sinn dahinter!
Wenn ich eine allgemeine Funktion machen will, dann schreib ich sie doch einfach in meine Hauptklasse und ruf sie in der main-funktion auf!!!

und was bringt mir ein visitor muster im bezug auf die darstellung einer Ordnerstruktur?! Wenn ich ein Composite habe, dann habe ich meine allgemeinen Funktionen doch sowieso in der abstrakten klasse!!!!!! wozu soll ich also noch ein Muster anlegen?!

lg, guni


----------



## maki (21. Jan 2008)

Composite tritt fast nur mit Visitor auf, die beiden gehören zusammen weil sie sich so gut ergänzen.



> Wenn ich eine allgemeine Funktion machen will, dann schreib ich sie doch einfach in meine Hauptklasse und ruf sie in der main-funktion auf!!!


Scherzkeks *g*

Visitor kann mehr als nur allgemeine Funktionen, es kann auch spezialisierte Funktionen anbieten.

Du müsstest jedesmal deine Tree Klasse ändern um funktionen hinzuzufügen, mit dem Visitor schreibst du dir einfach deinen eigenen speziellen Visitor, die Tree Klasse ändert sich nicht.

Abgesehen davon  ist der Visitor wirklich simpel, 2 Interfaces, hab ich dir schon geschrieben, C&P reicht.
Die Implementierung der Visitable Interfaces kann so auch stehenbleiben, du musst nur diene Funktionen in den Visitor schreiben.

Wenn dir printTree als einzige Funktion reicht, brauchst du keinen Visitor, wenn du jetzt noch suchen, kopieren, eine spezielle printTree Funktion etc. möchtest, ist Visitor den Freund.


----------



## guni (21. Jan 2008)

@manki ...

hmm - du bringst mich da auf ideen ....
vielleicht werde ich das doch mal angehn.
ich werd mal kurz schaun, was ein interface eigentlich ist, da ich erst seit 1 woche java mach und vorher nicht wirklich objektorientiert programmiert hab 

lg, guni


----------



## maki (21. Jan 2008)

> da ich erst seit 1 woche java mach und vorher nicht wirklich objektorientiert programmiert hab


rofl

Jeder muss mal anfangen.
Hoffe wir haben dich nicht zu sehr verwirrt.


----------



## guni (21. Jan 2008)

nana - passt schon.
vom konzept her war mir das ganze schon klar.
ich bin halt vorher ziemlich auf perl abgefahrn (linguistisch gesehn is es viel romantischer) aber da perl VOR der objektorierentierung entwicklet worden ist und sich diese Freaks vorgenommen haben die uralten compiler für neuen code kompatibel zu halten ist die objektorientierung etwas ... sagen wir mal: rudimentär.

vererbung, interfaces und so zeug mach ich jetzt eben *parktisch* erst mit java ...
aber es geht eh voran 
dank dem forum lern ich recht schnell und für das, dass ich vor einer woche noch nie java programmiert habe geht es eigentlich schon ganz flüssig von der hand *gg*

lg, guni


----------



## guni (21. Jan 2008)

@maki:
du schreibst:


> Solltest imho dafür sorgen, dass Tree als "Kinder" nur Trees enthalten kann, denn darum geht es beim Kompositium/Composite, d.h. Typisierte Listen, dann sparst du dir auch das ignorieren der unchecked Warnings.



das macht ja keinen Sinn! Schön, wenn das Composite-Muster das in seiner Philosophie vorsieht aber in der Praxis kann ich in eined Dateistruktur ja sowohl das Element _Ordner_ als auch das Element _Datei_ in einem Ordner haben!
Wie soll ich dass denn mit einer typisierten Liste lösen?!?

lg, guni


----------



## maki (21. Jan 2008)

Sowohl Datei als auch Ordner sollten eine gemeinsame Oberklasse haben, darum geht es beim Composite.
D.h. dein Typ sollte Tree sein, auch wenn ich diese Namensgebung etwas missglückt empfinde.


----------



## guni (21. Jan 2008)

> Sowohl Datei als auch Ordner sollten eine gemeinsame Oberklasse haben


das haben sie ja! 
deine Aussage war aber - wenn ich das richtig verstanden habe - dass man gegen die Philosophie vom Composite verstößt, wenn man Datei-*Objekte* und Ordner-*Objekte* in die gleiche Liste speichert. Und da stellt sich mir die Frage: gibt es da eine schönere Möglichkeit?


> auch wenn ich diese Namensgebung etwas missglückt empfinde


ja - ich weiß auch, dass der Name nicht wirklich das aussagt um das es geht!
mir is auf die gschwinde nix anderes eingfalln - hast an besseren vorschlag? ich bin für jede idee offen. außerdem kann ich dann gleich mal die Refactoring-Funktion von Eclipse wieder mal gut ausprobieren 

lg, guni


----------



## maki (21. Jan 2008)

> deine Aussage war aber - wenn ich das richtig verstanden habe - dass man gegen die Philosophie vom Composite verstößt, wenn man Datei-Objekte und Ordner-Objekte in die gleiche Liste speichert. Und da stellt sich mir die Frage: gibt es da eine schönere Möglichkeit?


Dann habe ich mich unklar ausgedrückt, die Listen sollten <Tree> enthalten.

Als Namen würde ich Node (Knoten) empfehlen, ist aber geschmackssache.


----------



## guni (21. Jan 2008)

danke für die Antwort.

habe jetzt gleich mal alles auf Node geändert - triffts einfach besser  

du schreibst:


> Dann habe ich mich unklar ausgedrückt, die Listen sollten <Tree> enthalten.


(inzwischen ist das ja <Node> *gg*) 
Jedenfalls kann ich den Listen gar nicht <Node> zuweisen, weil ich ja eine Liste von *Objekten* habe und <Node> ja eine abstrakte Klasse Klasse ist, von der sich die beiden Klassen Folder und File ableiten. Und ich kann ja - wenn ich das richtig verstanden habe - nur aus nicht-abstrakten Klassen Objekte erstellen, oder?! 
Das war ja eigentlich auch irgendwie der Sinn des Composites, dass man verschiedne Objekte in eine Struktur packt, die ähnlich aber eben doch nicht gleich sind ... oder versteh ich da was falsch?
Wie also soll ich <Node>-Objekte in meine Liste packen, das geht ja gar nicht!

zu deinem Code von Seite 1:

ich hab jetzt mal versucht, den Visitor ganz einfach zu realisieren.
Nach meinem Verständnis ist er ja ungefähr so aufgebaut:

auf "abstrakter ebene" gibt es 2 interfaces: visitor und visitable.
bei visitor hab ich eine methode _visit_ drin, bei visitable hab ich eine funktion _accept_ drin
als nächstes sag ich, dass meine node-klasse visitable implementiert und programmier die funktion accept dort aus.
dann schreib ich konkrete Visitorklassen (z.B.: PrintTree) die den abstrakten Visitor implementieren und sag dort dann, dass - in meinem Beispiel - der ganze Baum ausgegeben werden soll ...

in deiner Accept-Funktion schreibst du jetzt in Zeile 2 _nodeVisitor_ ... und ich steh schon wieder voll auf der Leitung. Wer is mein nodeVisitor? Muss da nicht _this_ rein? wo leg ich den nodeVisitor fest? Was muss da in meinem Fall wirklich stehen?


```
public void accept(Visitor visitor) {
    if (nodeVisitor.visit(this) == true) {
        for (Tree child : TreeList )
            child.accept(visitor);
        }
    }
}
```

lg, guni


----------



## maki (21. Jan 2008)

Die LinkedList sollte eigentlich typisiert sein, LinkedList<Node> eben.
Das geht, der Polymorphie sei dank 

NodeVisitor war meine Klasse, das Objekt habe ich deshalb nodeVisitor genannt (in Java schreibt man Variablen immer klein), bei dir wäre das visitor.

Über den Rückgabewert boolean kann man der accept Methode mitteilen, wann genug Nodes besucht wurden.

Die accept Methode ruft die visit Methode des Visitors auf, mit this als Parameter.
Das machen rekursiv alle Nodes, bis false zurückgegeben wird oder es keine Kinder mehr gibt.

Solltest den Unterschied zwischen Interfaces und abstrakten Klassen schon verstehen, ist wichtig.
Ich nehme an, du hast ein gutes Buch über Java.


----------



## guni (21. Jan 2008)

> Ich nehme an, du hast ein gutes Buch über Java.


nope - ich bezieh derzeit mein ganzes wissen aus dem gespräch mit dir 

aber den unterschied habe ich, denke ich schon verstanden - so rein aus dem anwendungkontext heraus:

die abstrakte klasse ist eine klasse, von der keine objekte erzeugt werden können; sie kann nur als Basisklasse für die Weitervererbung dienen

ein interface ist eine sammlung von methoden, die erst in einer klasse ausprogrammiert werden.

der unterschied ist vermutlich, dass eine abstrakte klasse doch schon mehr form hat wie ein interface ... schließlich gibt sie ja schon attribute mit, kann auch schon private methoden oder sogar ausprogrammierte public methoden enthalten; wenn sie all das nicht hat kann ich ja gleich wieder ein interface schreiben, oder   

habe meine funktion inzwischen fertiggestellt 

im Endeffekt schaut mein Konkreter Visitor jetzt so aus:

```
public class PrintTree implements Visitor {

	public boolean visit(Node host, String hostname) {
		for ( Object o : host.nodes){
			if (o instanceof Folder) {
				System.out.println(hostname+"/"+((Folder)o).getName());
				visit((Node)o, hostname+"/"+((Folder)o).getName());
			}
			if (o instanceof Link) {
				System.out.println(hostname+"/"+((Link)o).getName());
			}      
		}
		return false;
	}

}
```
wie ich allerdings meine Listen typisieren kann check ich noch immer nicht ...
meine int main schaut jetzt so aus:

```
public static void main(String[] args) throws SQLException, ClassNotFoundException{
	
	// visitForPrinting.visit(root);
	PrintTree visitForPrinting = new PrintTree();
	Folder root = new Folder("lsalarchiv");
	
	Folder Firma1 = new Folder("Firma1");
	Folder Firma2 = new Folder("Firma2");
	Folder AbtA = new Folder("Abteilung A");
	Folder AbtB = new Folder("Abteilung B");
	Folder AbtC = new Folder("Abteilung C");
	Folder AbtD = new Folder("Abteilung D");
	Folder AbtE = new Folder("Abteilung E");
	Folder AbtF = new Folder("Abteilung F");
	
	Link EmpA = new Link("Mitarbeiter A");
	Link EmpB = new Link("Mitarbeiter B");
	Link EmpC = new Link("Mitarbeiter C");
	Link EmpD = new Link("Mitarbeiter D");
	Link EmpE = new Link("Mitarbeiter E");
	Link EmpF = new Link("Mitarbeiter F");
	Link EmpG = new Link("Mitarbeiter G");
	Link EmpH = new Link("Mitarbeiter H");
	Link EmpI = new Link("Mitarbeiter I");
	Link EmpJ = new Link("Mitarbeiter J");
	Link EmpK = new Link("Mitarbeiter K");
	Link EmpL = new Link("Mitarbeiter L");
	Link EmpM = new Link("Mitarbeiter M");
	
	root.add(Firma1);
	root.add(Firma2);
	
	Firma1.add(AbtA);
	Firma1.add(AbtB);
	Firma1.add(AbtC);
	Firma1.add(AbtD);
	Firma2.add(AbtE);
	Firma2.add(AbtF);
	
	AbtA.add(EmpA);
	AbtA.add(EmpB);
	AbtA.add(EmpC);
	AbtB.add(EmpD);
	AbtB.add(EmpE);
	AbtB.add(EmpF);
	AbtC.add(EmpG);
	AbtC.add(EmpH);
	AbtC.add(EmpI);
	AbtE.add(EmpJ);
	AbtE.add(EmpK);
	AbtE.add(EmpL);
	AbtF.add(EmpM);
	
	root.accept(visitForPrinting);
	// Firma1.showMyContent();
	
	// root.printTree(root,root.getName());
	// createBaseStructure();	
}
```
da kann ich doch nicht Objekte von einer abstrakten Klasse erstellen!
deswegen sind meine Objekte immer Folder oder Links!!

wie kann ich also meine Listen typisieren?
lg, guni


----------



## maki (21. Jan 2008)

> die abstrakte klasse ist eine klasse, von der keine objekte erzeugt werden können; sie kann nur als Basisklasse für die Weitervererbung dienen
> 
> ein interface ist eine sammlung von methoden, die erst in einer klasse ausprogrammiert werden.


In Java können Objekte nur von einer einzigen Klasse erben, aber so viele Interfaces implementieren wie sie möchten.
Wenn man "extends" sagt, mein man "ist ein", während man bei Interfaces, also "implements", ausdrückt, dass eine Klasse etwas kann.



> im Endeffekt schaut mein Konkreter Visitor jetzt so aus:
> ....


Wozu schleifen?
Die visit Methode wird von jeder Node aufgerufen, rekursiv 
Lass das for mal weg, aber dann musst du natürlich auch true zurückgeben.

Den Sinn des hostname Parameters verstehe ich nicht ganz.



> wie ich allerdings meine Listen typisieren kann check ich noch immer nicht ...
> meine int main schaut jetzt so aus:


Zeig doch mal deine aktuelle Node Klasse.


----------



## guni (22. Jan 2008)

> Den Sinn des hostname Parameters verstehe ich nicht ganz.


den hostname-parameter habe ich eingebaut um den Namen meines Root-Folders mitzuübergeben ...


> Zeig doch mal deine aktuelle Node Klasse.


meine aktuelle Node-Klasse sieht so aus:

```
package pattern.structural.composite;

import java.util.LinkedList;
import java.util.ListIterator;
import pattern.behavioral.visitor.Visitable;
import pattern.behavioral.visitor.Visitor;

@SuppressWarnings("unchecked")
public abstract class Node 
implements Visitable{
	public LinkedList nodes; 
	Node parent;
	String name;
	
	public abstract boolean add(Node itemToAdd);
	public abstract boolean remove(Node itemToRemove);
	public abstract ListIterator createListIterator();

	public void accept(Visitor operation) {
		if (operation.visit(this,this.getName()) == true) {
			for (Object child : nodes)
				((Node)child).accept(operation);
		}
	}
	
	public void setParent(Node parentIn) {
		parent = parentIn;
	}	
	public Node getParent() {
		return parent;
	}
	public void setName(String nameIn) {
		name = nameIn;
	}
	
	public String getName() {
		return name;
	}
	
	public boolean NodeExists(String inName){
		ListIterator i = this.createListIterator();
		Node element;
		Boolean found = false;
		while (i.hasNext()){
			element = (Node)i.next();
			if (element.getName().equals(inName))
				found = true;
		}
		return found;
	}
	
	public void showMyContent(){
		ListIterator listIterator = this.createListIterator();
		Node currentTree;
		while (listIterator.hasNext()){
			currentTree = (Node)listIterator.next();
			System.out.println(currentTree.getName());
		}
	}
}
```
lg, guni


----------



## guni (22. Jan 2008)

HILFE! Wie bekommen ich die typisierten Listen hin????
lg, guni


----------



## maki (22. Jan 2008)

Das hier zB

```
public LinkedList nodes;
```
sollte so aussehen:

```
public LinkedList<Node> nodes;
```
So weis der Compiler, dass deine Liste nur Nodes enthalten darf.

Das gleiche gilt für alle Stellen, an denen du List oder LinkedList verwendest.

Deine Anno die die unchecked warnings unterdrückt solltest du rauswerfen.


----------



## guni (22. Jan 2008)

danke ...

habe jetzt übrigens auch deinen Rat befolgt und die konkrete Visit-Methode umgeschrieben.
da - wie du gesagt hast - eh jeder Knoten rekursiv besucht wird habe ich die schleife und alles rausgenommen und nur mehr eine Funktion der Form:

```
public boolean visit(Node host) {
	System.out.println(host.getName());
	if (host instanceof Folder){
		return true;
	} else {
		return false;
	}
}
```
vorher hat das viel komplizierter ausgesehn ... jetzt verstehe ich auch den Visitor wieder ein Stückchen mehr weil ich die Rekursion nur einmal lösen musste und in die konkreten Visit-Funktionen eigentlich nur die Logik reinschreiben muss, die den jeweiligen Knoten betrifft.

Was ich aber noch nicht ganz verstehe ist, wie ich jetzt in meine obenstehende Funktion wieder einbau, dass zu jedem Host immer der ganze Pfad ausgegeben wird ...

lg, guni

PS.: deinen Tipp mit der Node werd ich gleich umsetzten!


----------



## maki (22. Jan 2008)

Vielleicht gibt es eine Möglichkeit, dass du den doofen instanceOf operator loswirst  Polymorphie ist zu bevorzugen wo möglich.

Um den Pfad kümmern wir uns später, als Tipp: Du kannst doch in einer Schleife die Parents abfragen und deren Pfad so bekommen, zwar nciht performant, aber ne bessere Schnittstelle, für den Einstieg.


----------



## guni (22. Jan 2008)

> Vielleicht gibt es eine Möglichkeit, dass du den doofen instanceOf operator


ich sehe da eigentlich keine Möglichkeit. Schließlich muss ich meiner Funktion irgendwie sagen, dass sie bei einer Datei (Link) nicht mehr "weitergraben" darf ...
wenn ich die die Struktur dann mal im Echtsystem anleg (zum Glück hab ich jetzt ein Visitor-Pattern, da muss ich das Ganze nur mehr um einen konkreten Visitor erweitern :lol


> Du kannst doch in einer Schleife die Parents abfragen


klingt nicht allzu kompliziert ... das würd ich vielleicht sogar rekursiv schaffen

```
public String[] fullpath(Node node){
	Node current;
	if ((current = node.getParent) != null)
		fullpath(current);
}
```
vom Ansatz her dann noch ein Array.push oder sowas rein und das Ganze dann mit einer Funktion wrappen, die am Schluss ein 
Array.reverse und ein Array.impolde("/")
aufruft, oder? Geht das In Java? Wie könnte ich das in meine Rekursion einbauen?


> zwar nciht performant, aber ne bessere Schnittstelle, für den Einstieg


blenden wir mal eine Sekunde aus, dass ich ein kompletter Anfänger bin und tun so, als würde ich schon ewig Java können  ... was wäre dann sie eleganteste Lösung?

lg, guni


----------



## maki (22. Jan 2008)

> ich sehe da eigentlich keine Möglichkeit. Schließlich muss ich meiner Funktion irgendwie sagen, dass sie bei einer Datei (Link) nicht mehr "weitergraben" darf ...


Ich meine mich dunkel errinnern zu können, das es eine Möglichekit gibt, ob die hier funktioniert müsste man mal testen, das wäre ungefähr so:
Dein Visitor hat zwei Methoden, eine visit(Folder folder), und eine visit(File file).
Das gab es noch einen Haken glaube ich...
Ob und wie das geht müsste ich nochmals nachlesen, verschieben wir das auf später, und leben wir für einen Moment mit dem "hässlichen" instanceof operator.



> blenden wir mal eine Sekunde aus, dass ich ein kompletter Anfänger bin und tun so, als würde ich schon ewig Java können icon_wink.gif ... was wäre dann sie eleganteste Lösung?


Deine Lösung ist schon elegant.
Performanter aber etwas unflexibler wäre es, den Pfad nur bei der Erstellung zu berechnen.
Ich würde diene Lösung bevorzugen


----------



## guni (22. Jan 2008)

also geht das mit dem array.push / array.implode in java ?!
aber wie merke ich mir, was schon im String[] drinsteht?
wie muss ich da mein return statement aufbaun???

lg, guni


----------

