MINT üben

Objektorientierte Modellierung

Zehn mehrschrittige Aufgaben: Diagramme aus Texten erstellen, Assoziationen und Vererbung implementieren, Polymorphie und Referenzen verfolgen.

Dein Fortschritt:
0 / 0 Aufgaben
2

Aufgabenblock — AFB II

Zehn mehrschrittige Aufgaben, bunt gemischt aus allen Unterkapiteln: Klassendiagramme aus Texten erstellen, Klassen mit Assoziationen und Vererbung implementieren, Aufrufe mit Referenzen und überschriebenen Methoden verfolgen und Fehler im Quelltext erklären. Die gestuften Tipps helfen, wenn du nicht weiterkommst.

A1
Klassendiagramm aus einem Text: Stadtbibliothek
AFB II

Eine Stadtbibliothek beschreibt ihre Anforderungen so:

„Jedes Medium hat eine Signatur und einen Titel; beides ändert sich nach dem Erfassen nicht mehr. Man muss abfragen können, ob ein Medium ausgeliehen ist und von wem. Ein Leser hat eine Ausweisnummer und einen Namen. Er darf höchstens fünf Medien gleichzeitig ausleihen. Ein Leser kann ein Medium ausleihen und zurückgeben; außerdem soll man abfragen können, wie viele Medien er gerade hat.“

Erstellen Sie ein Klassendiagramm mit den Klassen Medium und Leser (Attribute, Konstruktoren, Methoden, Assoziation mit Kardinalitäten).

Ansatz: Markiere Substantive (Kandidaten für Klassen und Attribute) und Verben (Kandidaten für Methoden). „Ändert sich nicht“ heißt: Getter ja, Setter nein.
Beziehung: „von wem“ bedeutet: Ein Medium kennt seinen Leser (oder keinen → null). „höchstens fünf Medien“ spricht für eine Reihung vom Typ Medium mit Länge 5 und einen Zähler.
Kardinalitäten: Wie viele Leser hat ein Medium gleichzeitig höchstens? Wie viele Medien ein Leser?
Vollständige Lösung
Leser
  • - ausweisNr: Ganzzahl
  • - name: Zeichenkette
  • - medien: Reihung vom Typ Medium
  • - anzahl: Ganzzahl
  • c Leser(ausweisNr: Ganzzahl, name: Zeichenkette)
  • + ausleihen(m: Medium): Wahrheitswert
  • + zurueckgeben(m: Medium)
  • + getAnzahl(): Ganzzahl
leiht aus0..10..5
Medium
  • - signatur: Zeichenkette
  • - titel: Zeichenkette
  • - ausleiher: Leser
  • c Medium(signatur: Zeichenkette, titel: Zeichenkette)
  • + getSignatur(): Zeichenkette
  • + getTitel(): Zeichenkette
  • + istAusgeliehen(): Wahrheitswert
  • + getAusleiher(): Leser
  • + setAusleiher(l: Leser)

Begründungen: Signatur und Titel bekommen keinen Setter (nur lesbar). „Ausgeliehen“ braucht kein eigenes Attribut: istAusgeliehen() liefert ausleiher != null — so können die beiden Angaben nie widersprüchlich werden. ausleihen liefert einen Wahrheitswert, weil es scheitern kann (schon fünf Medien oder Medium schon verliehen). Ein Medium ist bei höchstens einem Leser (0..1), ein Leser hat 0 bis 5 Medien.

So könnte Leser implementiert werden:

public class Leser {
    private int ausweisNr;
    private String name;
    private Medium[] medien;
    private int anzahl;

    public Leser(int ausweisNr, String name) {
        this.ausweisNr = ausweisNr;
        this.name = name;
        medien = new Medium[5];
        anzahl = 0;
    }

    public boolean ausleihen(Medium m) {
        if (anzahl < medien.length && !m.istAusgeliehen()) {
            medien[anzahl] = m;
            anzahl = anzahl + 1;
            m.setAusleiher(this);
            return true;
        }
        return false;
    }

    public void zurueckgeben(Medium m) {
        for (int i = 0; i < anzahl; i++) {
            if (medien[i] == m) {
                medien[i] = medien[anzahl - 1];
                medien[anzahl - 1] = null;
                anzahl = anzahl - 1;
                m.setAusleiher(null);
                return;
            }
        }
    }

    public int getAnzahl() {
        return anzahl;
    }
}
A2
Klasse implementieren: Parkticket
AFB II

Ein Parkhaus rechnet so ab: Bis zu 15 Minuten Parkdauer ist das Parken kostenlos. Wird länger geparkt, kostet jede angefangene halbe Stunde der gesamten Parkdauer 150 Cent, höchstens aber 1200 Cent. Zeiten werden in Minuten seit Mitternacht gespeichert (8:40 Uhr = 520).

Parkticket
  • - einfahrt: Ganzzahl
  • - bezahlt: Wahrheitswert
  • c Parkticket(einfahrt: Ganzzahl)
  • + getEinfahrt(): Ganzzahl
  • + berechneGebuehr(ausfahrt: Ganzzahl): Ganzzahl
  • + bezahlen()
  • + istBezahlt(): Wahrheitswert

Implementieren Sie die Klasse. Ermitteln Sie außerdem die Gebühr in Cent für ein Ticket mit Einfahrt 8:40 Uhr bei Ausfahrt um 10:15 Uhr bzw. um 16:40 Uhr.

Cent
Cent
Ansatz: In berechneGebuehr zuerst die Parkdauer ausfahrt − einfahrt bestimmen, dann die drei Regeln nacheinander anwenden.
Angefangene halbe Stunden: Mit Ganzzahldivision rundet dauer / 30 ab. Der Trick (dauer + 29) / 30 rundet auf: 95 Minuten → 124 / 30 = 4 Takte.
Zahlen: 10:15 Uhr = 615, 16:40 Uhr = 1000. Vergiss die Deckelung nicht.
Vollständige Lösung
public class Parkticket {
    private int einfahrt;
    private boolean bezahlt;

    public Parkticket(int einfahrt) {
        this.einfahrt = einfahrt;
        bezahlt = false;
    }

    public int getEinfahrt() {
        return einfahrt;
    }

    public int berechneGebuehr(int ausfahrt) {
        int dauer = ausfahrt - einfahrt;
        if (dauer <= 15) {
            return 0;
        }
        int takte = (dauer + 29) / 30;
        int gebuehr = takte * 150;
        if (gebuehr > 1200) {
            gebuehr = 1200;
        }
        return gebuehr;
    }

    public void bezahlen() {
        bezahlt = true;
    }

    public boolean istBezahlt() {
        return bezahlt;
    }
}

10:15 Uhr: Dauer 615 − 520 = 95 min → 4 angefangene halbe Stunden → 4 · 150 = 600 Cent.
16:40 Uhr: Dauer 480 min → 16 Takte → 2400 Cent, gedeckelt auf 1200 Cent.

Wer mit dauer / 30 rechnet, erhält fälschlich 3 Takte (450 Cent). Einen Setter für einfahrt gibt es bewusst nicht — die Einfahrtszeit darf nachträglich nicht manipulierbar sein.

A3
Polymorphie verfolgen: Handytarife
AFB II

Ein Mobilfunkanbieter modelliert seine Tarife mit Vererbung. Die Klasse Tarif hat das geschützte Attribut grundgebuehr (#); Tarif.kosten(minuten) liefert grundgebuehr + minuten * 0.09.

public class Flatrate extends Tarif {
    public Flatrate(String name, double grundgebuehr) { super(name, grundgebuehr); }
    @Override
    public double kosten(int minuten) { return grundgebuehr; }
}
public class Kombi extends Tarif {
    private int freiminuten;
    public Kombi(String name, double g, int freiminuten) { super(name, g); this.freiminuten = freiminuten; }
    @Override
    public double kosten(int minuten) {
        if (minuten <= freiminuten) { return grundgebuehr; }
        return super.kosten(minuten - freiminuten);
    }
}
ArrayList<Tarif> t = new ArrayList<Tarif>();
t.add(new Tarif("Basis", 5.00));
t.add(new Flatrate("Flat", 19.99));
t.add(new Kombi("Kombi", 9.99, 100));
double s = 0;
for (int i = 0; i < t.size(); i++) {
    s = s + t.get(i).kosten(300);
}

Analysieren Sie den Ablauf und geben Sie die Werte in Euro an.

€
€
€
Ansatz: Bestimme für jeden Listenplatz, von welcher Klasse das Objekt ist, und nimm dessen kosten-Methode.
super: Im Kombi-Tarif rechnet super.kosten(200) mit der Formel aus Tarif — aber mit der Grundgebühr dieses Objekts.
Kontrolle: Basis: 5 + 300 · 0,09 = 32,00 €.
Vollständige Lösung

Basis: 5,00 + 27,00 = 32,00 €. Flat: 19,99 € (überschrieben, unabhängig von den Minuten). Kombi: 300 > 100, also super.kosten(200) = 9,99 + 200 · 0,09 = 27,99 €. Summe 79,98 €. Bei 80 Minuten liegt der Kombi-Tarif innerhalb der Freiminuten: 9,99 €.

Die Schleife kennt nur den Typ Tarif — dass drei verschiedene Methoden laufen, liegt an der dynamischen Bindung.