8 Baum in perfekter Komposition 8 Baum in perfekter Komposition Die Implementierung des Binärbaums im letzten Kapitel wird mithilfe des Entwurfsmusters Kompositum optimiert. Knoten und Abschluss Bei der einfach verketteten Liste wurde durch den Einsatz des Entwurfsmusters Kompositum die Struktur der Klassenbeziehungen verändert und eine optimierte Implementierung erreicht. 4 Wie funktioniert das Software-Entwurfsmuster bei der einfach verketteten Liste? Welche Sonderfälle vereinfacht es? Wie kann das Entwurfsmuster Kompositum bei Baumstrukturen angewendet werden? Welche Klassen benötigt man zur Umsetzung der Datenstruktur Binärbaum mithilfe des Entwurfsmusters Kompositum? In welchen Beziehungen stehen die Klassen zueinander? Jeder Knoten hat die Referenzattribute linkerNachfolger und rechterNachfolger. Gibt es keinen Nachfolger, so hat das Referenzattribut den Wert null. In Abbildung 1 wurden, im Gegensatz zu den vereinfachten Darstellungen im letzten Kapitel, bei jedem Knoten beide Pfeile als Symbol für die Referenzattribute eingetragen. 1 Darstellung des Binärbaums mit Abschlusssymbolen Wie im Kapitel 4 ist auch hier der Ansatz möglich, dass man anstelle von null als Wert der Referenzattribute für den linken und rechten Nachfolger eine Referenz auf ein Objekt der Klasse ABSCHLUSS setzt. Objekte der Klasse ABSCHLUSS haben weder Referenzattribute auf Nachfolger noch auf ein Datenelement, bieten jedoch die gleiche Schnittstelle in Form von Methoden an wie die Klasse KNOTEN. In Abbildung 2 sind die Objekte der Klasse ABSCHLUSS eingefügt. 2 Baum mit Objekten der Klasse ABSCHLUSS (blaue Einfärbung) Objekte der Klassen KNOTEN und ABSCHLUSS müssen nach außen die gleichen Methoden anbieten, somit die gleiche Schnittstelle realisieren. Im Vergleich zur Liste wird diese jedoch nicht LISTENELEMENT, sondern BAUMELEMENT genannt. Sowohl jeder Knoten als auch jedes Abschluss-Objekt ist ein Baumelement. Deshalb wird die Schnittstelle durch 75 II Die rekursive Datenstruktur Baum eine abstrakte Oberklasse realisiert. Das Klassendiagramm in Abbildung 3 gibt eine Übersicht über die beteiligten Klassen und deren Beziehungen. Das Entwurfsmuster Kompositum ist durch eine blaue Linienfarbe hervorgehoben. 3 Klassendiagramm der Binärbaumstruktur mit Kompositum Wie unterscheiden sich die Beziehungen des Klassendiagramms in Abbildung 3 von denen im Klassendiagramm, das in Abbildung 3 von Kapitel 4 die Datenstruktur Liste mithilfe des Entwurfsmusters Kompositum umsetzt? Welche Änderungen ergeben sich bei den Attributen der Klassen KNOTEN, BINBAUM und DATENELEMENT durch die Umstellung auf das Entwurfsmuster Kompositum? Implementierung des Entwurfsmusters Kompositum – Attribute und Konstruktoren BAUMELEMENT {abstrakt} Im ersten Schritt der Umsetzung werden nur die Attribute und Konstruktoren betrachtet. Die Klasse BAUMELEMENT hat keine Attribute. Somit müssen im Konstruktor keine BAUMELEMENT() Anweisungen ausgeführt werden, der Rumpf ist leer. ABSCHLUSS Die Klasse ABSCHLUSS hat keine Attribute. Wiederum müssen im Konstruktor keine Anweisungen ausgeführt werden. Im Sinne eines guten Programmierstils ruft man denABSCHLUSS() noch den Konstruktor der Oberklasse auf. BINBAUM Die Klasse KNOTEN hat wie bisher Referenzen auf den rechten und linken Nachfolger und ein Datenelement. Die Nachfolger sind entsprechend dem Klassendiagramm aus BAUMELELEMNT wurzel Abbildung 3 vom Datentyp BAUMELEMENT. BINBAUM() Der Konstruktor hat bisher beim Aufruf die eingegebene Referenz dem Referenzattribut daten zugewiesen und die Werte der beiden Nachfolgerreferenzen auf null KNOTEN gesetzt. Letzteres muss geändert werden: Man muss zwei Objekte der KlasDATENELEMENT daten se ABSCHLUSS erzeugen und deren Referenzen den Referenzattributen linBAUMELEMENT linkerNachfolger kerNachfolger und rechterNachfolger zuordnen (Abbildung 4). BAUMELEMENT rechterNachfolger Die Klasse BINBAUM hat wie bisher ein Referenzattribut wurzel. Dieses ist KNOTEN(DATENELEMENT) nun vom Typ BAUMELEMENT. Beim Erzeugen eines leeren Baums ist die KNOTEN(DATENELEMENT, KNOTEN, KNOTEN) Wurzel ein Objekt der Klasse ABSCHLUSS (Abbildung 5). 4 Alle beteiligten Objekte beim Erzeugen eines Knotens 5 Alle beteiligten Objekte beim Erzeugen eines Binärbaums 76 8 Baum in perfekter Komposition Suchen … Bei der Methode Suchen zeigt sich klar der Vorteil des Entwurfsmusters Kompositum. Alle bedingten Anweisungen, die prüfen, ob es einen Nachfolger gibt oder nicht, werden überflüssig. Dafür sind die Implementierungen der Methode Suchen in den Klassen ABSCHLUSS und KNOTEN unterschiedlich. - Suchen der Klasse KNOTEN: War die Suche bisher erfolglos, so wird bei einem der Nachfolger ein rekursiver Aufruf der Methode vorgenommen. Jeder Knoten hat jetzt immer zwei Nachfolger. - Suchen der Klasse ABSCHLUSS: Die Suche war erfolglos, es wird die leere Referenz zurückgegeben. Folgende Gegenüberstellung zeigt die Änderungen im Detail: vorher (einfacher Binärbaum) nachher (Binärbaum nach Entwurfsmuster Kompositum) Klasse KNOTEN Methode DATENELEMENT Suchen(String suchSchluessel) Klasse KNOTEN Methode DATENELEMENT Suchen(String suchSchluessel) wenn (daten.IstSchluesselGleich(suchSchluessel)) dann return daten sonst wenn (daten. IstSchluesselGroesserAls(suchSchluessel)) dann wenn (linkerNachfolger != null) dann return linkerNachfolger. Suchen(suchSchluessel) sonst return null endewenn sonst wenn (rechterNachfolger != null) dann return rechterNachfolger. Suchen(suchSchluessel) sonst return null endewenn endewenn endewenn wenn (daten.IstSchluesselGleich(suchSchluessel)) dann return daten sonst wenn (daten. IstSchluesselGroesserAls(suchSchluessel)) dann return linkerNachfolger. Suchen(suchSchluessel) sonst return rechterNachfolger. Suchen(suchSchluessel) endewenn endewenn Klasse ABSCHLUSS Methode DATENELEMENT Suchen(String suchSchluessel) return null 6 Veränderung der Methode Suchen in der Klasse KNOTEN durch die Umstellung auf das Entwurfsmuster Kompositum Die Methode Suchen der Klasse BINBAUM verkürzt sich ebenfalls. Es muss nicht mehr überprüft werden, ob der Baum leer ist, denn ein leerer Baum hat ein Abschluss-Objekt. Dieser gibt mit null die richtige Antwort auf eine Suchanfrage (siehe nächste Seite). 77 II Die rekursive Datenstruktur Baum vorher (einfacher Binärbaum) nachher (Binärbaum nach Entwurfsmuster Kompositum) Klasse BINBAUM Methode DATENELEMENT Suchen(String suchSchluessel) Klasse BINBAUM Methode DATENELEMENT Suchen(String suchSchluessel) wenn (wurzel != null) dann return wurzel.Suchen(suchSchluessel) sonst return null endewenn return wurzel.Suchen(suchSchluessel) 7 Veränderung der Methode Suchen in der Klasse BINBAUM durch die Umstellung auf das Entwurfsmuster Kompositum … und Einfügen Ein Einfügen findet beim geordneten Binärbaum immer am Ende eines Astes statt. Dort wird das Abschluss-Objekt ersetzt durch einen neuen Knoten. Er referenziert das einzufügende Datenelement und zwei Objekte der Klasse ABSCHLUSS. Abbildung 8 verdeutlicht dies am Beispiel des Methodenaufrufs wurzel.Einfuegen(Referenz auf Datenelement mit "cake" als Schlüssel). Der Abschluss a1 wird also durch einen neuen Knoten ersetzt, dessen Datenelement den Schlüssel "cake" hat und der zwei neue Abschlüsse referenziert. 8 Beim Einfügen wird ein Abschluss-Objekt (hier a1) durch einen neuen Knoten (hier mit der Beschriftung „cake“) mit zwei neuen Abschluss-Objekten (hier a4 und a5) ersetzt. Die Schwierigkeit bei dem in Abbildung 8 dargestellten Einfügevorgang ist, dass das Objekt "call" noch nicht weiß, dass es einen neuen linken Nachfolger erhalten wird, wenn es den Auftrag zum Einfügen an das Objekt a1 weitergibt. Das Objekt a1 erzeugt nun einen neuen Knoten "cake". Da a1 seinen Aufrufer, das Objekt "call", bisher aber nicht kennt, kann es ihn nicht über den neuen Nachfolger informieren. Wie kann der Aufrufer trotz der fehlenden Referenz nach oben die Information über den neuen Nachfolger erhalten? Man löst dieses Problem, indem man die Methode Einfuegen mit einem Rückgabewert vom Typ BAUMELEMENT versieht. Darüber kann jedes Objekt der Klasse ABSCHLUSS (hier das Objekt a1) dem Aufrufer-Objekt (hier der Knoten mit der Beschriftung "call") eine Referenz des von ihm erzeugten Knotens (hier der Knoten "cake") übergeben. Welchen Wert geben Objekte der Klasse KNOTEN beim Aufruf der Methode Einfuegen zurück? 78 8 Baum in perfekter Komposition Objekte der Klasse KNOTEN rufen zwar abhängig davon, ob das eigene Datenelement größer oder kleiner ist als die neuen Daten, die Methode Einfügen des linken oder rechten Nachbarn auf. Danach wird aber in beiden Fällen mit this die Referenz auf sich selbst zurückgegeben, da die Position jedes Knotens innerhalb der Baumstruktur gleich bleibt. Hinweise: - Das Verfahren, eine Information an den Aufrufer einer Methode durch die Einführung eines Rückgabewerts zu übergeben, wurde bereits bei der Methode EinfuegenVor bei Listen und bei der Methode Entfernen bei Bäumen angewandt. - Das Abschluss-Objekt a1 wird nach dem Einfügen (Abbildung 8 rechts) nicht mehr benötigt. In den meisten objektorientierten Programmiersprachen werden nicht mehr referenzierte Objekte von der U automatischen Speicherplatzbereinigung gelöscht. 3 7 U garbage collection Der Ablauf beim Einfügen des Wortes „cake“: wurzel.Einfuegen("cake") Daten sind gleich? nein Sind eigene Daten größer als die neuen Daten? ja Beauftrage linken Nachfolger mit dem Einfügen. wurzel.liNf.Einfuegen("cake") Erzeuge einen Knoten mit einem Datenelement "cake". Gib eine Referenz auf den erzeugten Knoten zurück. Setze den Rückgabewert als linken Nachfolger. Gib eine Referenz auf dich selbst zurück. 9 Ablauf des Methodenaufrufs wurzel.Einfuegen("cake") 79