Prof. aa Dr. J. Giesl Programmierung WS12/13 M. Brockschmidt, F. Emmes, C. Otto, T. Ströder Tutoraufgabe 1 (Seiteneffekte): Betrachten Sie das folgende Programm: public class TSeiteneffekte { public static void main ( String [] args ) { [] ws = new [2]; ws [0] = new (); ws [1] = new (); ws [0]. i = 2; ws [1]. i = 1; f(ws [1], ws [1], ws [0]); f(ws [1], ws ); // Speicherzustand hier zeichnen public static void f( w1,... ws) { int sum = 0; // Speicherzustand hier zeichnen for ( int j = 0; j < ws. length ; j ++) { w = ws[j]; sum += w.i; w.i = j + 2; ws [0] = w1; w1 = ws [1]; w1.i = -sum ; public class { public int i; Es wird nun die Methode main ausgeführt. Stellen Sie den Speicher (d.h. alle (implizit) im Programm vorkommenden Arrays (außer args) und alle Objekte) an folgenden Programmstellen graphisch dar: nach der Deklaration von sum in jedem Aufruf der Methode f vor Ende der main Methode Lösung: 1
[] Methode main ws [0] int i 2 Methode f... w1 ws sum 0 [1] [] [0] int i 1 [1] [] Methode main ws [0] int i -3 Methode f w1 ws... [1] int i 2 sum 0 [] Methode main ws [0] int i 2 [1] int i 1 Aufgabe 2 (Seiteneffekte): Betrachten Sie das folgende Programm: public class HSeiteneffekte { public static void main ( String [] args ) { w1 = new (); w2 = w1; (2 + 2 + 2 = 6 Punkte) w1.i = 1; w2.i = 2; 2
3... Programmierung WS12/13 int x = 3; int [] a = { 4, 5 ; f(w2, x, a); f(w1, x, 6, a [1]); // Speicherzustand hier zeichnen public static void f( w, int x, int... a) { // Speicherzustand hier zeichnen x = a [0]; a [1] = w.i; w.i = x; public class { public int i; Es wird nun die Methode main ausgeführt. Stellen Sie den Speicher (d.h. alle (implizit) im Programm vorkommenden Arrays (außer args) und alle Objekte) an folgenden Programmstellen graphisch dar: bei jedem Aufruf der Methode f vor Ende der main Methode Lösung: Methode main w1 w2 x a int i 2 int[] [0] 4 [1] 5 Methode f w x a 3 3
3... Programmierung WS12/13 w1 Methode main w2 x a int i 4 int[] [0] 4 [1] 2 Methode f w x a 3 int[] [0] 6 [1] 2 w1 int i 6 Methode main w2 x 3 int[] a [0] 4 [1] 2 Tutoraufgabe 3 (Refactoring): In dieser Tutor- und der nachfolgenden Hausaufgabe betrachten wir die Datei Geometry.java, welche Sie von unserer Webseite herunterladen müssen. Diese benötigt zusätzlich die Datei Point.java, welche ebenfalls auf unserer Webseite herunterzuladen ist. Beim sogenannten Refactoring geht es darum, die Syntax eines Programms zu verändern, dabei aber die Semantik des Programms unverändert zu belassen. Ziel des Refactorings ist es, den Programmcode hinsichtlich seiner Les- und Wartbarkeit zu verbessern. Die vorgegebene Datei Geometry.java enthält eine main Methode, welche ein durch seine Eckpunkte bestimmtes Polygon im zweidimensionalen Raum einliest und es anschließend auf der Konsole ausgibt. Das gesamte Programm befindet sich hierbei in der main Methode, welche dadurch groß und unübersichtlich ist. Ein besserer Programmierstil ist es, wenn das Programm in mehrere Abschnitte unterteilt und in verschiedenen Methoden implementiert ist. So wird die Hauptstruktur direkt ersichtlich, das Programm kann schneller verstanden und eventuelle Fehler können leichter gefunden werden. Um die Größe einer Methode zu messen, existieren verschiedene Metriken. Eine solche Metrik ist die NCSS (Non-Commenting Source Statements) Metrik. Diese zählt die Anweisungen, Verzweigungen und Schleifen innerhalb einer Methode. Setzt man voraus, dass jede Schleife und jede Verzweigung Blockanweisungen benutzt (also z. B. Verzweigungen der Form if (Bedingung) {Anweisung(en), aber nicht der Form if (Bedingung) Anweisung;), so entspricht diese Metrik der Anzahl der Semikola am Ende von Anweisungen und der öffnenden geschweiften Klammern innerhalb einer Methode (d. h. die erste öffnende geschweifte Klammer des Methodenrumpfes selbst zählt nicht dazu). Die main Methode des gegebenen Programms hat eine NCSS von 35. 4
a) Im ersten Teil des Programms werden die vier Variablen minx, maxx, miny und maxy initialisiert und in der anschließenden Schleife mit den Extremkoordinaten (Minimum und Maximum) in beiden Dimensionen belegt. Schreiben Sie eine Klasse ExtremeCoordinates, welche vier nicht-statische int Attribute minx, maxx, miny und maxy hat. Schreiben Sie in dieser Klasse zusätzlich eine statische Methode create() mit Rückgabetyp ExtremeCoordinates, welche ein neues Objekt dieses Typs anlegt, alle seine Attribute mit 0 belegt und dieses Objekt dann zurück liefert. b) Lagern Sie nun den ersten Teil des Programms (vom Kommentar Eingabe der Punkte des Polygons und Bestimmung der Extremkoordinaten bis zum Kommentar Ausgabe des Polygons auf der Konsole ) in eine statische Methode input(point[] polygon) mit Rückgabetyp ExtremeCoordinates aus. Ersetzen Sie dabei die Initialisierung der Variablen minx, maxx, miny und maxy durch einen Aufruf der create() Methode aus der Klasse ExtremeCoordinates und die Belegung der ersetzten Variablen durch eine Belegung der entsprechenden Attribute im erzeugten ExtremeCoordinates Objekt. Liefern Sie dieses Objekt am Ende der Methode input zurück. In der main Methode müssen Sie ebenfalls alle Vorkommen der ersetzten Variablen durch Zugriffe auf die entsprechenden Attribute des zurückgelieferten ExtremeCoordinates Objektes ersetzen. c) Lagern Sie nun den zweiten Teil des Programms (ab dem Kommentar Ausgabe des Polygons auf der Konsole ) in eine statische Methode output(extremecoordinates ex, Point[] polygon) ohne Rückgabe aus. Sie benötigen nun in der main Methode keine Variable mehr, welche das in der input Methode erzeugte ExtremeCoordinates Objekt speichert, sondern können den Aufruf der input Methode direkt als erstes Argument der output Methode nutzen. Die NCSS der main Methode soll anschließend 3 betragen. Nun spiegelt die main Methode genau die oben beschriebene Struktur des Programms wider. Lösung: // kapselt Minima und Maxima in zwei Dimensionen public class ExtremeCoordinates { public int maxx ; public int maxy ; public int minx ; public int miny ; // Initialisierung der Extremkoordinaten public static ExtremeCoordinates create () { ExtremeCoordinates res = new ExtremeCoordinates (); res. minx = 0; res. maxx = 0; res. miny = 0; res. maxy = 0; return res ; public class Geometry { // Eingabe der Punkte des Polygons und Bestimmung der Extremkoordinaten public static ExtremeCoordinates input ( Point [] polygon ) { ExtremeCoordinates res = ExtremeCoordinates. create (); for ( int i = 0; i < polygon. length ; i++) { // Einlesen eines Punktes System. out. println (" Geben Sie die X- Koordinate des " + (i + 1) + "-ten Punktes ein."); int x = Integer. parseint ( System. console (). readline ()); System. out. println (" Geben Sie die Y- Koordinate des " + (i + 1) + "-ten Punktes ein."); int y = Integer. parseint ( System. console (). readline ()); polygon [i] = Point. create (x, y); // Update der Extremkoordinaten res. minx = Math. min ( res. minx, polygon [i]. x); res. miny = Math. min ( res. miny, polygon [i]. y); res. maxx = Math. max ( res. maxx, polygon [i]. x); res. maxy = Math. max ( res. maxy, polygon [i]. y); return res ; // Liest ein Polygon ein und gibt es auf der Konsole aus public static void main ( String [] args ) { 5
// Eingabe der Anzahl Punkte, welche das Polygon bestimmen, und Anlegen des zugehoerigen Arrays System. out. println (" Geben Sie die Anzahl an Punkten ein, welche das Polygon bestimmen sollen."); Point [] polygon = new Point [ Integer. parseint ( System. console (). readline ())]; Geometry. output ( Geometry. input ( polygon ), polygon ); // Ausgabe des Polygons auf der Konsole public static void output ( ExtremeCoordinates ex, Point [] polygon ) { System. out. println (); for ( int i = ex. maxy ; i >= ex. miny ; i - -) { for ( int j = ex. minx ; j <= ex. maxx ; j ++) { Point p = Point. create (j, i); // Test, ob Punkt in Polygon liegt boolean in = false ; double sum = 0.0; for ( int k = 0; k < polygon. length ; k++) { // Berechnung des Winkels zwischen den Punkten polygon [i], p und // polygon [i + 1 % polygon. length ] // Ergebnis liegt zwischen -179 und 180 ( positive Werte zeigen einen Winkel gegen den // Uhrzeigersinn an) Point v1 = polygon [k]. sub (p); Point v2 = polygon [( k + 1) % polygon. length ]. sub (p); float signum = Math. signum ( v1. determinant ( v2 )); double angle = Math. todegrees ( Math. acos (( v1. scalarproduct (v2 )) / (v1. norm () * v2. norm ()))); angle = signum == 0? Math. round ( angle ) : signum * angle ; // Sonderfall : Punkt liegt auf einer Ecke oder Kante des Polygons if (p. equals ( polygon [k]) angle == 180) { in = true ; break ; // Aufsummierung der Winkel sum = sum + angle ; // Ist Betrag der Winkelsumme 360 ( Rundung zur Steigerung der Robustheit gegenueber // Rundungsfehlern ) oder ist der Sonderfall eingetreten, so liegt der Punkt innerhalb des // Polygons System. out. print (( in Math. abs ( Math. round ( sum )) == 360)? "#" : " "); System. out. println (); System. out. println (); Aufgabe 4 (Refactoring): (1 + 4 = 5 Punkte) In dieser Hausaufgabe soll das Refactoring aus der vorigen Tutoraufgabe weitergeführt werden. Die dort ausgelagerten Methoden input(point[] polygon) und output(extremecoordinates ex, Point[] polygon) sind immer noch recht komplex und können weiter in sinnvolle Unterabschnitte gegliedert werden. Außerdem wollen wir ein wiederholtes Auftreten des gleichen komplexen Ausdrucks (sogenannte Code-Duplikation) eliminieren. a) Schreiben Sie eine Methode readint() vom Typ int, welche einen vom Benutzer eingegebenen int Wert einliest. Ersetzen Sie sämtliche Vorkommen des Ausdrucks durch Aufrufe Ihrer neuen Methode. Integer.parseInt(System.console().readLine()) b) Lagern Sie weitere Methoden in der Datei Geometry.java aus (ohne die Semantik zu verändern), sodass die NCSS jeder Methode in der resultierenden Datei jeweils höchstens 8 beträgt. Hinweise: Die Programmkommentare liefern gute Hinweise, welche Abschnitte sinnvoll als Methoden ausgelagert werden können (überall da, wo mindestens 4 Anweisungen oder Blöcke zu einem Kommentar gehören, lässt sich die NCSS durch Methodenauslagerung reduzieren). Überlegen Sie sich, welche Variablen innerhalb eines Code-Abschnitts und nach einem Code-Abschnitt benötigt werden. Aus diesen Informationen lassen sich die Argumente und Rückgabetypen der Methoden erschließen. 6
In Methoden kann eine Schleife durch die return Anweisung direkt verlassen werden. Dadurch kann die Verwendung der Variablen in vom Typ boolean in Kombination mit der break Anweisung vermieden werden. Lösung: public class Geometry { // Berechnung des Winkels zwischen den Punkten polygon [i], p und polygon [i + 1 % polygon. length ] // Ergebnis liegt zwischen -179 und 180 ( positive Werte zeigen einen Winkel gegen den Uhrzeigersinn // an) public static double angle ( Point p1, Point p2, Point p3) { Point v1 = p1. sub ( p2 ); Point v2 = p3. sub ( p2 ); float signum = Math. signum ( v1. determinant ( v2 )); double angle = Math. todegrees ( Math. acos (( v1. scalarproduct ( v2 )) / ( v1. norm () * v2. norm ()))); return signum == 0? Math. round ( angle ) : signum * angle ; // Test, ob Punkt in Polygon liegt public static boolean inpolygon ( Point p, Point [] polygon ) { double sum = 0.0; for ( int k = 0; k < polygon. length ; k++) { double angle = Geometry. angle ( polygon [k], p, polygon [( k + 1) % polygon. length ]); // Sonderfall : Punkt liegt auf einer Ecke oder Kante des Polygons if (p. equals ( polygon [k]) angle == 180) { return true ; // Aufsummierung der Winkel sum = sum + angle ; // Ist Betrag der Winkelsumme 360 ( Rundung zur Steigerung der Robustheit gegenueber // Rundungsfehlern ), so liegt der Punkt innerhalb des Polygons return Math. abs ( Math. round ( sum )) == 360; // Eingabe der Punkte des Polygons und Bestimmung der Extremkoordinaten public static ExtremeCoordinates input ( Point [] polygon ) { ExtremeCoordinates res = ExtremeCoordinates. create (); for ( int i = 0; i < polygon. length ; i++) { polygon [i] = Geometry. readpoint ( " Geben Sie die X- Koordinate des " + (i + 1) + "-ten Punktes ein.", " Geben Sie die Y- Koordinate des " + (i + 1) + "-ten Punktes ein."); Geometry. updateextremecoordinates (res, polygon [i ]); return res ; // Liest ein Polygon ein und gibt es auf der Konsole aus public static void main ( String [] args ) { // Eingabe der Anzahl Punkte, welche das Polygon bestimmen, und Anlegen des zugehoerigen Arrays System. out. println (" Geben Sie die Anzahl an Punkten ein, welche das Polygon bestimmen sollen."); Point [] polygon = new Point [ Geometry. readint ()]; Geometry. output ( Geometry. input ( polygon ), polygon ); // Ausgabe des Polygons auf der Konsole public static void output ( ExtremeCoordinates ex, Point [] polygon ) { System. out. println (); for ( int i = ex. maxy ; i >= ex. miny ; i - -) { for ( int j = ex. minx ; j <= ex. maxx ; j ++) { System. out. print ( Geometry. inpolygon ( Point. create (j, i), polygon )? "#" : " "); System. out. println (); System. out. println (); // Einlesen eines Integers von der Konsole public static int readint () { return Integer. parseint ( System. console (). readline ()); // Einlesen eines Punktes public static Point readpoint ( String frstmsg, String scndmsg ) { System. out. println ( frstmsg ); int x = Geometry. readint (); System. out. println ( scndmsg ); int y = Geometry. readint (); 7
return Point. create (x, y); // Update der Extremkoordinaten public static void updateextremecoordinates ( ExtremeCoordinates ex, Point p) { ex. minx = Math. min ( ex. minx, p.x); ex. miny = Math. min ( ex. miny, p.y); ex. maxx = Math. max ( ex. maxx, p.x); ex. maxy = Math. max ( ex. maxy, p.y); Tutoraufgabe 5 (Blümchen): In dieser Aufgabe soll eine einfache Gartensimulation erstellt werden. Hierbei hat der Benutzer die Aufgabe, sich um das Wohlergehen verschiedener Blümchen zu kümmern, indem er diese mit der richtigen Menge Wasser versorgt. Hierbei ist es auch möglich, dass Blümchen mehr Wasser bekommen, als sie speichern können, und deswegen sterben. Weiterhin ist es möglich, nach Einsatz eines Bienchenschwarms durch Bestäubung neue Blümchen zu erzeugen. In dieser Simulation sollen mehrere Blümchenbeete verwaltet werden, wobei jedes Beet mehrere Blümchen enthalten kann. Sowohl für die Anzahl der Beete als auch für die Anzahl der Blümchen pro Beet ist eine Maximalkapazität vorgegeben. Die Simulation verläuft in Tagesschritten. Die folgende Ausgabe ist ein Beispiel für einen möglichen Tagesablauf: Tag 2 bricht an! Deine Beete sehen aktuell so aus: Beet 1: (1) Rittersporn [6/20] (Verbrauch: 6) (3) Sumpf-Schwertlilie [5/32] (Verbrauch: 5) (1) Rittersporn [12/20] (Verbrauch: 6) (3) Sumpf-Schwertlilie [10/32] (Verbrauch: 5) (1) Rittersporn [12/20] (Verbrauch: 6) (3) Sumpf-Schwertlilie [10/32] (Verbrauch: 5) --- Beet 2: (0) Tulpe [19/40] (Verbrauch: 7) (0) Tulpe [19/40] (Verbrauch: 7) --- Beet 3: (3) Sumpf-Schwertlilie [17/32] (Verbrauch: 5) (0) Tulpe [19/40] (Verbrauch: 7) --- Moechtest du Beet 1 giessen (Wassermenge: 12)? (1 fuer ja, sonst nein) 1 Das Bluemchen (1) Rittersporn [12/20] (Verbrauch: 6) wurde ertraenkt! Das Bluemchen (1) Rittersporn [12/20] (Verbrauch: 6) wurde ertraenkt! Moechtest du Beet 2 giessen (Wassermenge: 12)? (1 fuer ja, sonst nein) 0 Moechtest du Beet 3 giessen (Wassermenge: 12)? (1 fuer ja, sonst nein) 0 Deine Beete sehen aktuell so aus: Beet 1: (1) Rittersporn [18/20] (Verbrauch: 6) (3) Sumpf-Schwertlilie [17/32] (Verbrauch: 5) (3) Sumpf-Schwertlilie [22/32] (Verbrauch: 5) (3) Sumpf-Schwertlilie [22/32] (Verbrauch: 5) --- Beet 2: (0) Tulpe [19/40] (Verbrauch: 7) 8
(0) Tulpe [19/40] (Verbrauch: 7) --- Beet 3: (3) Sumpf-Schwertlilie [17/32] (Verbrauch: 5) (0) Tulpe [19/40] (Verbrauch: 7) --- Moechtest du die Bienchen losschicken? (1 fuer ja, sonst nein) 1 In welchem Beet sollen die Bienchen starten? 2 In welchem Beet sollen die Bienchen ihre Reise beenden? 3 Durch das Wunder der Bestaeubung ist das neue Bluemchen (3) Sumpf-Schwertlilie [15/32] (Verbrauch: 5) entstanden! Durch das Wunder der Bestaeubung ist das neue Bluemchen (3) Sumpf-Schwertlilie [15/32] (Verbrauch: 5) entstanden! Druecke Enter, um den naechsten Tag zu beginnen. Der Typ eines jeden Blümchens ist durch den Aufzählungstypen (enum) Typ realisiert. In dieser Aufgabe wird auch häufig der Ordinalwert (berechnet durch typ.ordinal()) verwendet. Beachten Sie folgende Anforderungen an das System: Die Simulation soll in einzelnen Tagen ablaufen, wobei pro Tag folgende Schritte in dieser Reihenfolge durchgeführt werden: 1. Der aktuelle Tag wird angezeigt. 2. Alle Blümchen verbrauchen eine Tagesration Wasser. 3. Der Zustand der Beete wird ausgegeben. Bei jedem Blümchen wird jeweils der Ordinalwert des Typs (z.b. (1)), der Name (z.b. Rittersporn), der aktuelle Wasservorrat des Blümchens (z.b. 6), der maximale Wasservorrat, den das Blümchen verträgt (z.b. 20) sowie sein Tagesverbrauch an Wasser (z.b. Verbrauch: 6) angegeben. 4. Die Beete werden nach Wunsch des Benutzers gegossen. Hierdurch erhöht sich die Wassermenge jedes Blümchens im Beet um 12 Einheiten. Falls dadurch der maximal erlaubte Wasservorrat des Blümchens überschritten wird, stirbt es. 5. Der Zustand der Beete wird ausgegeben. 6. Auf Wunsch des Benutzers wird der Bienchenschwarm losgeschickt. Die Simulation endet, wenn kein Blümchen mehr existiert. Pro Tag wird der Wasserverbrauch der einzelnen Blümchen simuliert, indem der Wasservorrat pro Blümchen um den jeweiligen Tagesverbrauch verkleinert wird. Gibt es für ein Blümchen beim Wasserverbrauchen nicht genug Wasser in seinem aktuellen Vorrat, stirbt es und existiert ab diesem Zeitpunkt nicht mehr im entsprechenden Beet. Jeden Tag ist es pro Beet möglich, alle Blümchen in diesem Beet zu gießen. Es ist nicht möglich, nur einzelne Blümchen zu gießen, es werden immer alle Blümchen des Beetes gegossen. Wird ein Blümchen so stark gegossen, dass der Maximalvorrat überschritten wird, stirbt es und existiert ab diesem Zeitpunkt nicht mehr im entsprechenden Beet. Pro Tag ist es einmal möglich, den Bienchenschwarm loszuschicken. Dieser fliegt von einem Beet A zu einem Beet B, wobei A und B identisch sein dürfen. Für jedes Blümchenpaar a A und b B wird ein neues Blümchen in Beet B erzeugt, sofern der Ordinalwert des Typs von Blümchen a echt kleiner als der Ordinalwert des Typs von Blümchen b ist. Der Typ des neuen Blümchen ist durch (typ(a) + typ(b)) % MAX definiert, wobei typ(a) und typ(b) den Ordinalwert des Typs von Blümchen a bzw. b angeben und MAX die Anzahl verschiedener Blümchentypen ist (die verschiedenen Ordinalwerte der Typen sind also 0,..., MAX 1). Aus den Blümchen a A mit typ(a) = 2 und b B mit typ(b) = 3 9
entsteht also mit MAX = 4 ein Blümchen vom Typ (2 + 3) % 4 = 5 % 4 = 1. Beachten Sie, dass neue Blümchen erst zum Schluss dieser Prozedur zum Blümchenbeet B hinzugefügt werden! Wenn in einem Beet kein Platz für ein neues Blümchen ist, wird dieses neue Blümchen ignoriert und nicht eingepflanzt (Jedes Beet kann maximal 9 Blümchen enthalten). In den Informationen, die zu einem einzelnen Blümchen angezeigt werden, müssen folgende Angaben enthalten sein: Ordinalwert des Typs, Name, aktueller Wasservorrat, maximaler Wasservorrat, Verbrauch pro Tag. Das Sterben eines Blümchens kann dadurch realisiert werden, dass der entsprechende Speichereintrag auf null gesetzt wird. Jedes neue Blümchen hat am Anfang genug Wasser für exakt drei Tage. Wenn der erlaubte maximale Wasservorrat des Blümchen hierfür nicht groß genug ist, wird der erlaubte Wasservorrat stattdessen komplett gefüllt. Sie brauchen hier nicht alles selber zu programmieren, sondern sollen auf ein bereits erstelltes Programmgerüst zurückgreifen, welches Sie bei den Materialien zu dieser Übung von der Webseite der Vorlesung herunterladen können. Dieses besteht aus den Dateien Zufall.java (für Zufallszahlen), Bluemchen.java (welche ein Blümchen darstellt), Typ.java (welche die unterschiedlichen Typen von Blümchen darstellt), Bienchen.java (welche den Bienchenschwarm darstellt), Bluemchenbeet.java (welche ein Blümchenbeet darstellt) und Garten.java (welche die Benutzerschnittstelle zur Verfügung stellt). In den Dateien Bluemchen.java, Bienchen.java und Bluemchenbeet.java sind einige Stellen mit // TO DO markiert. An diesen Stellen sollen Sie Ihre eigene Implementierung einfügen. Modifizieren Sie keine anderen Stellen im Code und legen Sie auch keine weiteren Klassen bzw. Dateien an. Sie dürfen Hilfsmethoden zu den Klassen hinzufügen. Bedenken Sie bei der Konzeption Ihrer Lösung, welche der Attribute und Methoden in den von Ihnen erstellten Klassen statisch sein sollten. Hier dürfen (noch) alle Methoden und Attribute public sein. Beginnen Sie Ihre Bearbeitung mit den folgenden drei Methoden: Bluemchen.neuesBluemchen Bluemchen.toString Bluemchenbeet.neuesBluemchen Lösung: Die Lösung finden Sie in den Files Bienchen.java, Bluemchenbeet.java und Bluemchen.java. Aufgabe 6 (Müll): (1 + 1 + 1 + 3 + 1 + 1 + 3 + 3 = 14 Punkte) Wir beschäftigen uns in dieser Aufgabe mit einer Müllverwertungsfabrik. In dieser Fabrik wird Müll mit Müllwagen angeliefert und auf Müllbänder verteilt. Auf der Homepage zur Veranstaltung finden Sie die Dateien Muell.java, Fabrik.java, Wagen.java und Band.java. Laden Sie diese bitte herunter und verändern Sie diese wie in den folgenden Aufgabenteilen beschrieben. a) Für den Müll unterscheiden wir die Sorten Rest, Metall und Plastik. Schreiben Sie einen Aufzählungstypen (enum) Sorte, der diese drei Sorten abbildet. b) Ein Stück Müll bilden wir durch ein Objekt der Klasse Muell ab. Für jedes solche Objekt merken wir uns die Sorte und ob der Müll sauber ist. Hierfür existieren in der Klassendatei schon entsprechende Felder. Erweitern Sie die Klasse Muell um eine nicht-statische Methode tostring(), die (je nach Sorte des Mülls und der Sauberkeit) eine lesbare String-Darstellung wie in den folgenden Beispielen zurückgibt. Metall Plastik Rest Plastik (sauber) 10
c) In der Klasse Wagen ist die Methode zufallswagen vorgegeben, an der Sie nichts verändern müssen und sollen. Diese Methode erzeugt ein Objekt der Klasse Wagen, der einen Müllwagen mit zufälligem Müll als Inhalt repräsentiert. Hierfür wird die statische Methode fuersorte in der Klasse Muell aufgerufen, die noch nicht existiert und von Ihnen implementiert werden soll. Die Methode fuersorte bekommt einen int-wert von 0 bis einschließlich 2 und gibt abhängig von diesem Wert ein neues Muell-Objekt zurück. Dieses Objekt ist nicht sauber und hat die Sorte, die durch das int-argument definiert ist. Wenn also beispielsweise fuersorte mit dem Argument 1 aufgerufen wird und Plastik die zweite Sorte Müll ist, soll ein neues Muell-Objekt mit der Sorte Plastik zurückgegeben werden. Hinweis: Benutzen Sie die statische Methode values(), die in jedem Aufzählungstypen existiert und ein Array der einzelnen Aufzählungswerte zurückgibt. Das Argument können Sie als Index für dieses Array benutzen. d) Ein Müllband repräsentieren wir durch die Klasse Band. Die Datei enthält bereits die Deklaration eines Attributs vom Typ Muell[], das (analog zu den Wagen) benutzt wird, um den auf dem Band befindlichen Müll zu speichern. Für die nachfolgenden Aufgabenteile möchten wir uns anzeigen lassen, was für Müll in einem Wagen bzw. auf einem Band ist. Da beide Klassen Band und Wagen ein Array vom Typ Muell[] zum Speichern benutzen, bietet es sich an, eine Hilfsmethode zu schreiben. Diese Hilfsmethode soll ein Argument vom Typ Muell[] bekommen und einen String zurückliefern, der wie in folgendem Beispiel aufgebaut ist: Metall, Plastik (sauber), Rest, Rest, Plastik Für den Ergebnis-String werden also die tostring()-ausgaben der einzelnen Muell-Objekte entsprechend miteinander verbunden. Schreiben Sie diese Hilfsmethode mit dem Namen arraytostring in der Klasse Band und deklarieren Sie diese als static, wenn dies Sinn macht. Hinweis: Beachten Sie, dass das Array auch null-einträge enthalten kann und überspringen Sie diese! Erweitern Sie anschließend die Klassen Band und Wagen jeweils um eine Methode tostring(), die einen String entsprechend obiger Beschreibung zurückgibt. Verwenden Sie hierfür jeweils die Hilfsmethode! e) Für den Betrieb der Fabrik muss der Müll aus dem Wagen auf die Bänder verteilt werden. Schreiben Sie hierfür zuerst eine nicht-statische Methode leere() in der Klasse Wagen. Diese Methode soll ein Muell- Objekt aus dem inhalt-array zurückgeben und den Array-Eintrag auf null setzen. Nach entsprechend oft wiederholter Anwendung dieser Methode soll das Array also nur noch null enthalten. Falls das Array kein Muell-Objekt mehr enthält, soll die Methode leere() null zurückgeben. f) Schreiben Sie in der Klasse Band eine Methode drauf ohne Rückgabe. Diese Methode soll ein Muell- Objekt als Argument bekommen und dieses an einer freien Stelle (also mit Inhalt null) im inhalt-array des Bandes speichern. Wenn das Array keinen null-eintrag mehr enthält, soll nichts passieren. g) Verwenden Sie nun an der mit TODO markierten Stelle in der Klasse Fabrik die Methoden leere und drauf, um den Müll aus dem Wagen auf die Bänder bandeins und bandzwei zu verteilen. Wechseln Sie hierbei nach jedem Muell-Objekt das Band, so dass das erste, dritte,... Objekt dem ersten Band und das zweite, vierte,... Objekt dem zweiten Band hinzugefügt wird. Hinweis: Beachten Sie wieder, dass das Array auch null-einträge enthalten kann und überspringen Sie diese! Geben Sie vorher und nach jedem Hinzufügen aus, welchen Inhalt die Bänder und der Wagen haben. Orientieren Sie sich hierfür an der folgenden Beispielausgabe: Wagen: Rest, Plastik, Plastik, Metall, Plastik, Plastik, Rest, Metall, Plastik, Rest Band 1: Band 2: Wagen: Plastik, Plastik, Metall, Plastik, Plastik, Rest, Metall, Plastik, Rest 11
Band 1: Rest Band 2: Wagen: Plastik, Metall, Plastik, Plastik, Rest, Metall, Plastik, Rest Band 1: Rest Band 2: Plastik Wagen: Metall, Plastik, Plastik, Rest, Metall, Plastik, Rest Band 1: Rest, Plastik Band 2: Plastik Wagen: Plastik, Plastik, Rest, Metall, Plastik, Rest Band 1: Rest, Plastik Band 2: Plastik, Metall Wagen: Plastik, Rest, Metall, Plastik, Rest Band 1: Rest, Plastik, Plastik Band 2: Plastik, Metall Wagen: Rest, Metall, Plastik, Rest Band 1: Rest, Plastik, Plastik Band 2: Plastik, Metall, Plastik Wagen: Metall, Plastik, Rest Band 1: Rest, Plastik, Plastik, Rest Band 2: Plastik, Metall, Plastik Wagen: Plastik, Rest Band 1: Rest, Plastik, Plastik, Rest Band 2: Plastik, Metall, Plastik, Metall Wagen: Rest Band 1: Rest, Plastik, Plastik, Rest, Plastik Band 2: Plastik, Metall, Plastik, Metall Wagen: Band 1: Rest, Plastik, Plastik, Rest, Plastik Band 2: Plastik, Metall, Plastik, Metall, Rest h) Nachdem der Müll nun auf die Bänder verteilt ist, soll die Fabrik damit etwas machen. Schreiben Sie die Methode bearbeite ohne Rückgabe in der Klasse Fabrik. Diese Methode soll ein Array vom Typ Muell[] übergeben bekommen und darin alle Muell-Objekte der Sorte Metall entfernen (im Array durch null ersetzen) alle Muell-Objekte der Sorte Plastik reinigen (siehe unten) alle Muell-Objekte der Sorte Rest ignorieren alle null-einträge ignorieren Zum Reinigen der Plastik-Objekte schreiben Sie in der Klasse Muell die Methode reinige() und verwenden diese. Die Methode hat kein Argument und keine Rückgabe und setzt das Attribut sauber des jeweiligen Muell-Objekts auf true. Rufen Sie die Methode bearbeite nach Ihrem bisherigen Code in der main-methode der Klasse Fabrik auf und übergeben Sie das Muell-Array des zweiten Bandes. Geben Sie anschließend den Inhalt dieses Bandes aus. Daraus ergibt sich (als Fortsetzung des vorherigen Beispiels) diese Ausgabe: Band 2: Plastik (sauber), Plastik (sauber), Rest Lösung: Die Lösung finden Sie in den Files Band.java, Fabrik.java, Sorte.java und Wagen.java. 12