Noch mehr Java gibts auf www.javahochzwei.de

Kompositum – Entwurfsmuster

Es gibt mit Sicherheit leichtere Dinge auf der Welt als das Entwurfsmuster „Kompositum“ zu verstehen… ;)

Heute wage ich mich mal daran, Euch das zu erklären:

In der Literatur ist immer von s.g. „Teil-Ganzes-Hierarchien“ die Rede. Aber was ist das? Stellen wir uns ein Dateisystem (engl. filesystem) vor, z.B. ist der Windows Explorer ein solches: Es gibt Ordner, die nur Dateien enthalten, welche die leer sind oder wiederrum andere Ordner beinhalten, für die gleiches gilt. Die Ordner können also -theoretisch- unendlich verschachtelt sein, wobei deren Inhalt jeweils variieren kann.

Möchte ich nun von C:/ aus, dem „Wurzelordner“, die ganze Festplatte durchgehen und mir immer die Dateinamen in den jeweiligen Ordnern ausgeben lassen, stehe ich vor einem Problem. Ich weiß ja nicht wie „tief“ jeweils die Unterordner von Ordner x gehen, bzw. ob er überhaupt Unterordner hat, oder nur Dateien oder ist er gar leer.

Es ist also extrem ungewiss, welcher Inhalt uns in den jeweiligen Ordnern begegnen wird, daher wäre es extrem gut, wenn folgendes ginge: Behandel jeden Ordner gleich, egal was für einen Inahtl er hat (also weitere Unterordner, Dateien, Dateien und Ordner oder einen leeren Inhalt).

Jetzt kommt das Kompositum-Entwurfsmuster ins Spiel! In Wikipedia wird es wie folgt dargestellt:

Lasst Euch von diesem allgemeinen Entwurf nicht abschrecken!

Gehen wir zurück zu unserem Dateisystem und überlegen uns nun, was von der Grafik oben was bedeutet. Wir können uns vorstellen, das Blatt wäre eine Datei. Das Kompositum ein Ordner. Und die Komponente? Die müssen wir uns abstrakt als einen Verzeichniseintrag vorstellen! Ein Verzeichniseintrag kann sowohl eine Datei (also ein Blatt) oder auch ein Ordner (also ein Kompositum) sein. Soweit so gut!

Jetzt ist es aber so, dass es eine beliebige Verschachtelung von Ordnern und/oder Dateien geben kann. Grundlage dieser Verschachtelung ist aber immer ein großer Ordner, in Windows wäre das C:/, die Festplatte. Also ist in diesem Fall auch C:/ ein Kompositum, also ein Ordner!

Jeder Ordner (Kompositum) muss allerdings jetzt wissen, welchen Inhalt er besitzt. Möglicher Inhalt kann wie gesagt weitere Dateien und/oder Ordner sein. Man müsste also in der Klasse Ordner (Kopositum) jeweils ein Array (oder ArrayList, Vector etc.) für Ordner und eine für Dateien halten. Stellen wir uns vor, wir würden, wenn das alles implementiert ist, dem Ordner noch eine weitere Inhaltsoption geben. Dann müssten wir nachträglich in die Klasse Ordner noch ein weiteres Array einfügen etc. Ihr merkt also schon, dass es -will man das System erweitern- viele Probleme geben kann.

Man sagt also, um das eben geschilderte Problem zu beheben, dass ein Ordner (Kompositum) immer nur Verzeichniseinträge (Komponente) besitzen kann. Verzeichniseinträge selbst können in unserem Beispiel selbst wieder Ordner oder Dateien sein. Ein Ordner weiß also nur, dass er -eine beliebige Menge von- Verzeichniseinträgen in sich trägt. Dies in Java dargestellt ginge wie folgt:

package kompositum;

import java.util.Iterator;
import java.util.Vector;

/**
 * @author Armin
 * @since 13.09.2012
 * 
 *        info@prog-blog.de www.prog-blog.de
 */
public class Ordner extends Verzeichniseintrag {

	private Vector<Verzeichniseintrag> verzeichniseintraege;

	public Ordner() {
		verzeichniseintraege = new Vector<Verzeichniseintrag>();
	}

	public void addVerzeichniseintrag(Verzeichniseintrag eintrag) {
		verzeichniseintraege.add(eintrag);
	}
}

Beim Initialisieren eines Objekts der Klasse Ordner erstellen wir einen leeren Vector -also einen leeren Ordner. Durch die Methode addVerzeichniseintrag(…) geben wir aber die Möglichkeit einen Verzeichniseintrag (also eine Datei oder wiederrum einen Ordner) hinzuzufügen.

Jetzt schauen wir uns mal die abstrakte Klasse Verzeichniseintrag an:

package kompositum;

/**
 * @author Armin
 * @since 13.09.2012
 * 
 * info@prog-blog.de
 * www.prog-blog.de
 */
public abstract class Verzeichniseintrag {

	protected String name;

	protected abstract void getName();
}

Ihr fragt Euch, warum ist diese abstrakt? Naja, es spielt ja eine Rolle, welchen konkreten Typ vn Verzeichniseintrag wir haben: Datei oder Ordner. Also können wir die getName() erst später in den konkreten Klassen implementieren.

Die Klasse Datei sieht relativ einfach aus:

package kompositum;

/**
 * @author Armin
 * @since 13.09.2012
 * 
 * info@prog-blog.de
 * www.prog-blog.de
 */
public class Datei extends Verzeichniseintrag {

	public Datei(String name) {
		this.name = name;
	}

	@Override
	protected void getName() {

		System.out.println(name);
	}
}

Die getName() gibt lediglich den Namen der Datei zurück!

Wie muss aber die getName der Klasse Ordner aussehen, was muss sie tun? Sie muss ja jeweils die Namen der in ihr drin liegenden Verzeichniseinträge aufzählen. Da es aber hier eine mögliche tiefere Verschachtelung gibt (Unterordner von Unterordner…) passiert dies rekursiv:

package kompositum;

import java.util.Iterator;
import java.util.Vector;

/**
 * @author Armin
 * @since 13.09.2012
 * 
 *        info@prog-blog.de www.prog-blog.de
 */
public class Ordner extends Verzeichniseintrag {

	private Vector<Verzeichniseintrag> verzeichniseintraege;

	public Ordner(String name) {
		this.name = name;
		verzeichniseintraege = new Vector<Verzeichniseintrag>();
	}

	public void addVerzeichniseintrag(Verzeichniseintrag eintrag) {
		verzeichniseintraege.add(eintrag);
	}

	@Override
	protected void getName() {

		Iterator<Verzeichniseintrag> it = verzeichniseintraege.iterator();

		while (it.hasNext()) {
			it.next().getName();
		}
	}

}

Die Methode geht mit Hilfe eines Iterators rekursiv immer tiefer in die Ordnerstruktur rein und gibt dann erst die Namen der Dateien im jeweiligen Ordner aus, wenn dieser keinerlei Unterordner mehr besitzt. Ist dies passiert, geht er wiederum eine Rekursionsebene höher und durchforstet den drüberliegenden Ordner auf weiteren Inhalt.

Jetzt wollen wir uns noch ein Beispiel des ganzen anschauen. Hierzu haben wir eine Klasse Klient:

package kompositum;

/**
 * @author Armin
 * @since 13.09.2012
 * 
 * info@prog-blog.de
 * www.prog-blog.de
 */
public class Klient {

	public static void main(String[] args) {

		// erstellt einen Wurzelordner "C:/"
		Ordner wurzel = new Ordner("C:/");

		// erstellt einen Ordner "Bilder/"
		Ordner bilder = new Ordner("Bilder/");

		// und fügt diesen dem Ordner C:/ ein -> also C:/Bilder/
		wurzel.addVerzeichniseintrag(bilder);

		// nun füllen wir den Bilderordner
		bilder.addVerzeichniseintrag(new Datei("Urlaubsbild1.jpg"));
		bilder.addVerzeichniseintrag(new Datei("Urlaubsbild2.jpg"));
		bilder.addVerzeichniseintrag(new Datei("Urlaubsbild3.jpg"));
		bilder.addVerzeichniseintrag(new Datei("Urlaubsbild4.jpg"));

		// nun fügen wir noch in den Wurzelordner selbst eine Datei ein
		wurzel.addVerzeichniseintrag(new Datei("DateiImWurzelverzeichnis"));

		// und lassen uns das alles ausgeben:
		wurzel.getName();
	}
}

Die Ausgabe ist dann folgende:

Urlaubsbild1.jpg
Urlaubsbild2.jpg
Urlaubsbild3.jpg
Urlaubsbild4.jpg
DateiImWurzelverzeichnis

Und fertig ist unsere erste Implementierung eines Kompositummusters! :)

 

P.S.: Eine kleine Anmerkung noch zum Schluss! Java verwendet dieses Muster bspw. in javax.swing.JComponent (Swing). Hier ist es so, dass ein JPanel Elemente wie JButtons, JLabels etc. enthalten kann, oder eben wiederrum einen JPanel, der beliebige Elemente enthalten kann.


Quelle: http://www.philipphauer.de/

 

Quelltext herunterladen:

  Kompositum.rar (1,5 KiB, 104 hits)

4 comments

  1. Violet sagt:

    Major thanks for the post. Want more.

  2. DunKing sagt:

    Echt gut erklärt, danke vielmals!

  3. rudi sagt:

    super erklärt:) besten Dank

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.