Benutzer-Werkzeuge

Webseiten-Werkzeuge


listen:kompositum:start

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen RevisionVorhergehende Überarbeitung
listen:kompositum:start [2024/09/19 09:58] – [Implementierung einer Liste mit dem Entwurfsmuster "Kompositum"] Martin Pabstlisten:kompositum:start [Unbekanntes Datum] (aktuell) – gelöscht - Externe Bearbeitung (Unbekanntes Datum) 127.0.0.1
Zeile 1: Zeile 1:
-====== Entwurfsmuster allgemein ====== 
- 
-<WRAP center round info 80%> 
-**Entwurfsmuster** (englisch **design patterns**) sind bewährte Lösungsschablonen (hier: Klassen und deren Beziehungen) für wiederkehrende Entwurfsprobleme. Ihre Verwendung sorgt für leichter wartbaren Programmcode. Sie reduziert damit den Aufwand und auch die Kosten bei der Softwareentwicklung. \\ \\  
-//Ausgangspunkt der Verwendung von Entwurfsmustern in der Softwaretechnik war die Veröffentlichung des Buches "Design Patterns" durch Erich Gamma, Richard Helm, Ralph Johnson und John Vlissides im Jahr 1994. Sie tragen den Ehrennamen "Gang of four", entsprechend ist das Buch als "GoF-Book" bekannt. Auch das Entwurfsmuster "Kompositum" wird in diesem Buch beschrieben.//   
-</WRAP> 
- 
-===== Implementierung einer Liste mit dem Entwurfsmuster "Kompositum" ===== 
-Der Nachfolger des letzten Elementes der Liste soll nicht mehr den Wert ''null'' erhalten, sondern auf ein Abschluss-Objekt zeigen. Das wird uns etwas später die Möglichkeit geben, auf elegante Art und Weise ohne den fehlerträchtigen Wert ''null'' auszukommen. Sehen wir uns eine beispielhafte Liste mit vier Elementen im Objektdiagramm an: 
- 
-{{ :listen:kompositum:objektdiagramm_einfach_verkettete_liste_kompositum_.svg |}} 
- 
-Damit das Attribut ''nachfolger'' sowohl auf ein ''Knoten''-Objekt als auch auf ein ''Abschluss''-Objekt zeigen kann, muss sein Datentyp eine Oberklasse von ''Knoten'' und ''Abschluss'' sein. Wir nennen diese Klasse ''Listenelement'' und implementieren sie als **abstrakte Klasse**. Alle Methoden der Klasse ''Listenelement'' sind abstrakt, d.h. sie werden dort nur deklariert, aber nicht mit Anweisungen gefüllt. Das geschieht erst in den Klassen ''Knoten'' und ''Abschluss'' -- und zwar auf unterschiedliche Weise! 
- 
-==== Klassendiagramm ==== 
-{{ :datenstrukturen:kompositum:pasted:20210918-153717.png?400 }} 
- 
-==== Implementierung ==== 
-Die Klasse ''Inhalt'' soll beispielhaft einfach nur Text speichern können: 
-<code learnj> 
-public class Inhalt { 
-    
-   private String text; 
- 
-   public Inhalt(String text) { 
-      this.text = text; 
-   } 
-    
-   public void ausgabe() { 
-      println(text); 
-   } 
-} 
-</code> 
- 
-Die Klasse ''Listenelement'' besitzt drei abstrakte Methoden (d.h. Methoden, die erst in Unterklassen definiert werden): 
-<code learnj> 
-abstract class Listenelement { 
-   public abstract Inhalt getInhalt();                         // gibt den Inhalt zurück (null im Falle des Abschlusses) 
-   public abstract Listenelement getNachfolger();              // gibt das nachfolgende Element zurück (null im Falle des Abschlusses) 
-   public abstract Listenelement hintenAnfügen(Inhalt inhalt); // fügt den Inhalt am Ende der Liste an.  
-    
-   // Bemerkung: 
-   // Bei Knoten-Objekten gibt die Methode hintenAnfügen das Objekt selbst wieder zurück. 
-   // Bei Abschluss-Objekten gibt die Methode das neue eingefügte Knoten-Objekt zurück. 
-} 
-</code> 
- 
-Die Klasse ''Liste'' speichert eine Referenz "''erster''" auf das erste Element der Liste. Ihre Methoden machen regen Gebrauch von den Methoden dieses Objekts. 
- 
-<code learnj> 
-class Liste { 
- 
-   private Listenelement erster;    // Zeigt auf das erste Listenelement 
-     
-   public Liste() { 
-      erster = new Abschluss();     // Die leere Liste besteht nur aus einem Abschluss-Objekt 
-   } 
- 
-   public Inhalt erstenEntnehmen() {  
-      Listenelement alterErster = erster; 
-      erster = erster.getNachfolger(); 
-      return alterErster.getInhalt(); 
-   } 
-    
-   public void hintenAnfügen(Inhalt inhalt) { 
-      // Genau dann, wenn erster ein Abschluss-Objekt ist, wird die 
-      // folgende Anweisung den Wert von erster ändern. 
-      erster = erster.hintenAnfügen(inhalt); 
-   } 
-} 
-</code>  
- 
-Ein Objekt der Klasse ''Knoten'' speichert eine Referenz auf ein Inhalt-Objekt sowie eine Referenz auf das nachfolgende Knoten-Objekt (bzw. ein Abschluss-Objekt, wenn der Knoten das letzte Element der Liste ist). 
- 
-<code learnj> 
-class Knoten extends Listenelement { 
-   private Inhalt inhalt; 
-   private Listenelement nachfolger; 
- 
-   public Listenelement getNachfolger() { 
-      return nachfolger; 
-   } 
-     
-   public Knoten(Inhalt inhalt, Knoten nachfolger) { 
-      this.inhalt = inhalt; 
-      this.nachfolger = nachfolger; 
-   } 
-     
-   public Knoten(Inhalt inhalt, Listenelement l) { 
-      this.inhalt = inhalt; 
-      nachfolger = l; 
-   } 
- 
-   public Inhalt getInhalt() { 
-      return inhalt; 
-   } 
-    
-   public Listenelement hintenAnfügen(Inhalt inhalt) { 
-      nachfolger = nachfolger.hintenAnfügen(inhalt);     // "Soll sich doch das Nachfolger-Objekt darum kümmern!" :-) 
-      return this; 
-   } 
- 
-} 
-</code> 
-  
-Das ''Abschluss''-Objekt der Liste hat keinen Inhalt. Seine Hauptaufgabe besteht darin, sich in der Methode ''hintenAnfügen'' um die Erzeugung und das Einflicken eines neuen ''Knoten-Objekts'' in die Liste zu kümmern. 
-<code learnj> 
-class Abschluss extends Listenelement { 
-   public Inhalt getInhalt() { 
-      return null; 
-   } 
-     
-   public Listenelement getNachfolger() { 
-      return this; 
-   } 
- 
-   public Listenelement hintenAnfügen(Inhalt inhalt) { 
-      Knoten k = new Knoten(inhalt, this);        // erzeugt einen neuen Knoten, dessen nachfolger-Objekt dieses Abschluss-Objekt ist 
-      return k;                                   // gibt eine Referenz auf den neuen Knoten an das aufrufende Objekt zurück 
-  } 
-} 
-</code> 
- 
-===== Das ganze Programm zum Ausprobieren ===== 
-<HTML> 
- 
-<div class="java-online" style="height: 600px; width: 100%" data-java-online="{'withBottomPanel': true, 'id': 'kompositum1', 'speed': 100000}"> 
- 
-<script type="text/plain" title="Hauptprogramm.java"> 
-Liste liste = new Liste(); 
- 
-liste.hintenAnfügen(new Inhalt("Erster")); 
-liste.hintenAnfügen(new Inhalt("Zweiter")); 
-liste.hintenAnfügen(new Inhalt("Dritter")); 
- 
-Inhalt i = liste.erstenEntnehmen(); 
-i.ausgabe(); 
- 
-i = liste.erstenEntnehmen(); 
-i.ausgabe(); 
- 
-i = liste.erstenEntnehmen(); 
-i.ausgabe(); 
-</script> 
- 
-<script type="text/plain" title="Listenelement.java"> 
-abstract class Listenelement { 
-   public abstract Inhalt getInhalt();                         // gibt den Inhalt zurück (null im Falle des Abschlusses) 
-   public abstract Listenelement getNachfolger();              // gibt das nachfolgende Element zurück (null im Falle des Abschlusses) 
-   public abstract Listenelement hintenAnfügen(Inhalt inhalt); // fügt den Inhalt am Ende der Liste an.  
-    
-   // Bemerkung: 
-   // Bei Knoten-Objekten gibt die Methode hintenAnfügen das Objekt selbst wieder zurück. 
-   // Bei Abschluss-Objekten gibt die Methode das neue eingefügte Konten-Objekt zurück. 
-} 
-</script> 
- 
-<script type="text/plain" title="Liste.java"> 
-class Liste { 
- 
-   private Listenelement erster;    // Zeigt auf das erste Listenelement 
-     
-   public Liste() { 
-      erster = new Abschluss();     // Die leere Liste besteht nur aus einem Abschluss-Objekt 
-   } 
- 
-   public Inhalt erstenEntnehmen() {  
-      Listenelement alterErster = erster; 
-      erster = erster.getNachfolger(); 
-      return alterErster.getInhalt(); 
-   } 
-    
-   public void hintenAnfügen(Inhalt inhalt) { 
-      // Genau dann, wenn erster ein Abschluss-Objekt ist, wird die 
-      // folgende Anweisung den Wert von erster ändern. 
-      erster = erster.hintenAnfügen(inhalt); 
-   } 
-} 
-</script> 
- 
-<script type="text/plain" title="Knoten.java"> 
-class Knoten extends Listenelement { 
-   private Inhalt inhalt; 
-   private Listenelement nachfolger; 
- 
-   public Listenelement getNachfolger() { 
-      return nachfolger; 
-   } 
-     
-   public Knoten(Inhalt inhalt, Knoten nachfolger) { 
-      this.inhalt = inhalt; 
-      this.nachfolger = nachfolger; 
-   } 
-     
-   public Knoten(Inhalt inhalt, Listenelement l) { 
-      this.inhalt = inhalt; 
-      nachfolger = l; 
-   } 
- 
-   public Inhalt getInhalt() { 
-      return inhalt; 
-   } 
-    
-  public Listenelement hintenAnfügen(Inhalt inhalt) { 
-      nachfolger = nachfolger.hintenAnfügen(inhalt); 
-      return this; 
-  } 
- 
-} 
-</script> 
- 
-<script type="text/plain" title="Abschluss.java"> 
-class Abschluss extends Listenelement { 
-   public Inhalt getInhalt() { 
-      return null; 
-   } 
-     
-   public Listenelement getNachfolger() { 
-      return this; 
-   } 
- 
-   public Listenelement hintenAnfügen(Inhalt inhalt) { 
-      Knoten k = new Knoten(inhalt, this); 
-      return k; 
-  } 
-} 
-</script> 
- 
-<script type="text/plain" title="Inhalt.java"> 
-class Inhalt { 
-    
-   private String text; 
- 
-   public Inhalt(String text) { 
-      this.text = text; 
-   } 
-    
-   public void ausgabe() { 
-      println(text); 
-   } 
-} 
-</script> 
- 
-</div> 
- 
-</HTML> 
- 
-===== Aufgaben zur Listenimplementierung mit Kompositum ===== 
-[[.listenaufgaben:start|Hier einige Aufgaben zur Listenimplementierung mit Kompositum!]] 
- 
-===== Trennung von Struktur und Inhalt ===== 
-<WRAP center round info 60%> 
-Im obigen Beispiel muss die Inhaltsklasse nicht verändert werden, wenn die Funktionalitäten der Liste programmiert werden. Das Prinzip **"Trennung von Struktur und Inhalt"** ist damit erfüllt. // //  
- 
-**Bemerkung:** Die Trennung von Struktur und Inhalt wäre nicht gegeben, wenn man der Inhaltsklasse selbst das Attribut "nachfolger" und die Methoden ''getNachfolger'' sowie ''hintenAnfügen'' hinzugefügt hätte.  
-</WRAP> 
- 
-====== Das Entwurfsmuster "Kompositum" ====== 
-Die Kernidee des Entwurfsmusters "Kompositum" besteht darin, dass in einer zusammengesetzten Datenstruktur die "innen liegenden" Objekte (Fachbegriff: Kompositum, im obigen Beispiel: Knoten) und die "außen liegenden" Objekte (Fachbegriff: Blatt, im obigem Beispiel: Abschluss) Unterklassen einer abstrakten Klasse (Fachbegriff: Komponente, im obigen Beispiel: Listenelement) sind. In dieser abstrakten Klasse werden alle Methoden deklariert, die zum Verändern der Datenstruktur und zum Zugriff auf einzelne Elemente benötigt werden. Dadurch können die Kompositum-Objekte und die Blatt-Objekte im Hauptprogramm "gleich behandelt" werden.  
- 
-<WRAP center round info 80%> 
-Das **Entwurfsmuster Kompositum** (engl. composite) dient als Lösungsansatz für Situationen, in denen sowohl einfache als auch zusammengesetzte Elemente gleichbehandelt werden sollen und in denen eine Teil-Ganzes-Beziehung vorliegt. Es wird allgemein durch folgendes Klassendiagramm veranschaulicht: 
-{{ :datenstrukturen:kompositum:pasted:20210914-213631.png?400 }} \\  
-**Bemerkung:** Die Bezeichnung "Blatt" in diesem Diagramm ist recht unglücklich, da sie nicht übereinstimmt mit dem Fachbegriff "Blatt" für außenliegende Knoten eines Baumes (siehe das Kapitel über [[datenstrukturen:baeume:start|Bäume]]). Letzterer wird weit häufiger gebraucht. Im Englischen ist die Nomenklatur geringfügig besser: //Leaf// bezeichnet die Blatt-Klasse im Entwurfsmuster Kompositum, //leaf node// einen außenliegenden Knoten eines Baumes. 
-</WRAP> 
- 
-===== Beispiel: Dateisystem ===== 
-Eine Möglichkeit zur anschaulichen Anwendung des Kompositums auf eine vertraute Struktur bietet das Dateisystem, das auf einem Laufwerk (Festplatte, USB-Stick, …) zu finden ist: 
-{{ :datenstrukturen:kompositum:pasted:20210914-213835.png }} 
-Wie gewohnt kann ein Laufwerk hier Dateien und Ordner enthalten, ein Ordner wiederum (Unter-)Ordner und Dateien (die keine weiteren Objekte enthalten). Gemeinsame Methoden könnten z.B. der Ermittlung der Größe von Dateien und Ordnern dienen. Die Klasse Laufwerk steht außerhalb des Entwurfsmusters Kompositum. 
- 
-===== Vorteile/Nachteile ===== 
-Gegenüber einer Implementierung mit Knoten-Objekten aber ohne Abschluss-Objekt ergeben sich folgende Vorteile/Nachteile: \\ \\  
-**Vorteile:** \\ 
-  * Gleichbehandlung der unterschiedlichen Listenbestandteile möglich 
-  * Keine Überprüfung auf null-Werte mehr 
-  * Stärkere Strukturierung 
-**Nachteile:** \\ 
-  * Programmieraufwand ist höher 
-  * Mehr beteiligte Klassen 
-  * deutlich schlechtere Performance 
  
listen/kompositum/start.1726739884.txt.gz · Zuletzt geändert: 2024/09/22 04:37 (Externe Bearbeitung)

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki