studyklar
GdI 2 · Übungsblatt 6 · Aufgabe 2

Abstrakte Klasse Figur über Kreis und Dreieck

Kreise und Dreiecke sollen auf eine Zeichenfläche. Beide Klassen haben viel gemeinsam. Eine abstrakte Oberklasse Figur nimmt das Gemeinsame auf. Danach lassen sich alle Figuren in einem Array speichern und mit einer Schleife verschieben und vergrößern. Rechts steht das fertige Programm, die Zeilen zum aktuellen Schritt sind hervorgehoben. Mit Weiter geht es Schritt für Schritt voran.

Die Aufgabe

Aufgabe 2

Es wird wieder die Klasse grafik.Zeichenflaeche( ) mit den statischen Methoden zeichnePixel( ) und leere( ) verwendet. Es sollen Dreiecke und Kreise gezeichnet werden. Der Code zum Zeichnen eines Dreiecks und zum Zeichnen eines Kreises findet sich in der Klasse grafik.GrafikApp.

a) Erstellen Sie eine Klasse Dreieck (Sie können die Klasse von der Übung 4 aus Übungsblatt 5 kopieren) und eine entsprechende Klasse Kreis. Die beiden Klassen sollen auch jeweils eine verschiebe-Methode haben, die analog zur Übung 4 aus Übungsblatt 5 arbeitet.

b) Kreis und Dreieck sollen nun Unterklassen einer gemeinsamen abstrakten Oberklasse „Figur“ werden. Überlegen Sie sich, welche Eigenschaften (Attribute) und Methoden die beiden Klassen gemeinsam haben (und was in einem gemeinsamen Konstruktor angelegt werden könnte) und verlagern Sie diese in die Oberklasse. Methoden mit identischem Code sollten Sie komplett in die Oberklasse verlagern, für Methoden mit unterschiedlichem Code aber gleicher Aufgabe und gleichen Parametern sollten Sie abstrakte Methoden in der Oberklasse definieren.

c) Es soll nun auch möglich sein, Kreise und Dreiecke um einen bestimmten Faktor zu skalieren. Z. B. soll bei einer Skalierung um den Faktor 2 der Radius bzw. die Seitenlänge verdoppelt und anschließend die betreffende Figur neu gezeichnet werden. Sehen Sie auch hierfür eine abstrakte Methode in der Klasse Figur vor.

d) Legen Sie nun in der main-Methode von GrafikApp mehrere Kreise und Dreiecke an und speichern Sie sie in einem gemeinsamen Array ab. Anschließend sollen alle Figuren gemeinsam um einen bestimmten x- und einen bestimmten y-Wert verschoben werden.

e) Skalieren Sie alle Figuren mit demselben Faktor.

Aus Blatt 5 gibt es die Klasse Dreieck. Dazu kommt ein Kreis. Dann bekommen beide eine gemeinsame Oberklasse Figur. Zum Schluss werden sechs Figuren angelegt, verschoben und vergrößert.

Merke: Gleicher Code kommt ganz in die Oberklasse. Gleiche Aufgabe mit verschiedenem Code wird in der Oberklasse eine abstrakte Methode: nur der Kopf, ohne Inhalt. Jede Unterklasse muss sie dann selbst schreiben.
  1. a) Kreis nach dem Muster des Dreiecks.
  2. b) Gemeinsames in die abstrakte Klasse Figur verschieben.
  3. c) Eine abstrakte Methode skaliere.
  4. d) Alle Figuren in einem Array, gemeinsam verschieben.
  5. e) Alle Figuren gemeinsam skalieren.

Die vorgegebene Klasse Zeichenflaeche liegt uns nicht vor. Zum Prüfen haben wir einen kleinen Ersatz geschrieben. Er hat dieselben Methoden zeichnePixel(x, y) und leere(), die Fläche ist 900 · 800 Pixel groß. Die x-Achse zeigt nach rechts, die y-Achse nach oben. Statt eines Fensters druckt die neue Methode zeige() die Fläche auf die Konsole: Ein # steht für ein Feld von 15 · 30 Pixeln, in dem mindestens ein Pixel gezeichnet ist, ein Punkt für ein leeres Feld. Alle Klassen stehen hier in einer Datei GrafikApp.java, nur GrafikApp ist public.

Die Klasse Kreis

public class Kreis {     private int xMitte, yMitte, radius;     public Kreis(int xMitte, int yMitte, int radius) {        this.xMitte = xMitte;        this.yMitte = yMitte;        this.radius = radius;        zeichne();    }     public void verschiebe(int deltaX, int deltaY) {        xMitte += deltaX;        yMitte += deltaY;        GrafikApp.aktualisiere();    }     public void zeichne() {        for (double alpha = -Math.PI; alpha <= Math.PI; alpha += 0.01) {            int xLinie = xMitte + (int) (radius * Math.cos(alpha));            int yLinie = yMitte + (int) (radius * Math.sin(alpha));            Zeichenflaeche.zeichnePixel(xLinie, yLinie);        }    }}

Links steht der Kreis nach Teil a, als eigene Datei Kreis.java. Er ist gebaut wie das Dreieck: Mittelpunkt (xMitte, yMitte), eine Größe (hier radius), ein Konstruktor, der gleich zeichnet, dazu verschiebe und zeichne.

zeichne läuft einmal um den Kreis. Der Winkel alpha geht in kleinen Schritten von 0,01 von −π bis π, also einmal ganz herum. Zu jedem Winkel liegt der Punkt auf dem Kreis bei x = xMitte + radius · cos(alpha) und y = yMitte + radius · sin(alpha). (int) schneidet die Nachkommastellen ab, weil ein Pixel ganze Koordinaten hat.

Aus Blatt 5, Aufgabe 4 („Dreiecke als Objekte: zeichnen, verschieben, neu zeichnen“): Dreieck merkt sich Mittelpunkt und Seitenlänge und zeichnet sich im Konstruktor. verschiebe ändert den Mittelpunkt und ruft aktualisiere() auf: Fläche leeren, alle Figuren neu zeichnen.

Zwei Arrays, zweimal derselbe Code

    private static Dreieck[] dreiecke = new Dreieck[10];    private static Kreis[] kreise = new Kreis[10];
    public static void aktualisiere() {        Zeichenflaeche.leere();        for (Dreieck dreieck : dreiecke) {            if (dreieck != null) {                dreieck.zeichne();            }        }        for (Kreis kreis : kreise) {            if (kreis != null) {                kreis.zeichne();            }        }    }

Nach Teil a hat GrafikApp.java je ein Array für Dreiecke und für Kreise (Zeilen 3 und 4 dieser Fassung). Darum muss aktualisiere zwei Arrays durchlaufen. Das Programm läuft so. Aber vieles steht doppelt da: die Attribute xMitte und yMitte, die Methode verschiebe Wort für Wort, und die Schleife hier. Käme ein Rechteck dazu, bräuchte man ein drittes Array und eine dritte Schleife.

Teil b löst das. Rechts ist schon die fertige Fassung hervorgehoben: ein Array, eine Schleife.

Noch 10 Schritte bis zum Ergebnis
Den ganzen Lösungsweg sehen Studierende und Lehrende der HS KL. Registriere dich mit deiner Adresse @stud.hs-kl.de oder @hs-kl.de, dann geht es hier direkt weiter.

Das Programm GrafikApp

Figur, Kreis, Dreieck und main

Abbildung 1Das Programm GrafikApp

Figur, Kreis, Dreieck und main

abstract class Figur {     protected int xMitte, yMitte;     public Figur(int xMitte, int yMitte) {        this.xMitte = xMitte;        this.yMitte = yMitte;    }     public abstract void zeichne();     public abstract void skaliere(double faktor);     public void verschiebe(int deltaX, int deltaY) {        xMitte += deltaX;        yMitte += deltaY;        GrafikApp.aktualisiere();    }} class Kreis extends Figur {     private int radius;     public Kreis(int xMitte, int yMitte, int radius) {        super(xMitte, yMitte);        this.radius = radius;        zeichne();    }     @Override    public void zeichne() {        for (double alpha = -Math.PI; alpha <= Math.PI; alpha += 0.01) {            int xLinie = xMitte + (int) (radius * Math.cos(alpha));            int yLinie = yMitte + (int) (radius * Math.sin(alpha));            Zeichenflaeche.zeichnePixel(xLinie, yLinie);        }    }     @Override    public void skaliere(double faktor) {        radius *= faktor;        GrafikApp.aktualisiere();    }} class Dreieck extends Figur {     private int seitenlaenge;     public Dreieck(int xMitte, int yMitte, int seitenlaenge) {        super(xMitte, yMitte);        this.seitenlaenge = seitenlaenge;        zeichne();    }     @Override    public void zeichne() {        // Bestimmung linke Ecke:        int x = xMitte - seitenlaenge / 2;        int y = yMitte - (int) Math.round(seitenlaenge * 0.289);        // Untere Linie:        for (int deltaX = 0; deltaX <= seitenlaenge; deltaX++) {            Zeichenflaeche.zeichnePixel(x + deltaX, y);        }        // Seiten:        for (double deltaX = 0; deltaX < seitenlaenge / 2; deltaX += 0.5) {            int deltaY = (int) (1.732 * deltaX);            Zeichenflaeche.zeichnePixel(x + deltaX, y + deltaY);            Zeichenflaeche.zeichnePixel(x + seitenlaenge - deltaX - 1, y + deltaY);        }    }     @Override    public void skaliere(double faktor) {        seitenlaenge *= faktor;        GrafikApp.aktualisiere();    }} public class GrafikApp {     private static Figur[] figuren = new Figur[10];     public static void main(String[] args) {        legeFigurenAn();        Zeichenflaeche.zeige();        verschiebeFiguren(200, 100);        Zeichenflaeche.zeige();        skaliereFiguren(1.5);        Zeichenflaeche.zeige();    }     public static void legeFigurenAn() {        figuren[0] = new Kreis(200, 200, 80);        figuren[1] = new Kreis(500, 200, 80);        figuren[2] = new Kreis(200, 400, 80);        figuren[3] = new Kreis(500, 400, 80);        figuren[4] = new Dreieck(350, 300, 200);        figuren[5] = new Dreieck(350, 500, 200);    }     public static void verschiebeFiguren(int deltaX, int deltaY) {        for (Figur figur : figuren) {            if (figur != null) {                figur.verschiebe(deltaX, deltaY);            }        }    }     public static void skaliereFiguren(double faktor) {        for (Figur figur : figuren) {            if (figur != null) {                figur.skaliere(faktor);            }        }    }     public static void aktualisiere() {        Zeichenflaeche.leere();        for (Figur figur : figuren) {            if (figur != null) {                figur.zeichne();            }        }    }}
LegendeDas ganze Programm.