MINT üben

Objektorientierte Modellierung

Zehn mehrschrittige Aufgaben: Diagramme erstellen, Klassen implementieren, Referenzen verfolgen und Reihungen von Objekten auswerten.

Dein Fortschritt:
0 / 0 Aufgaben
2

Aufgabenblock — AFB II

Zehn mehrschrittige Aufgaben, bunt gemischt aus allen Unterkapiteln: Klassendiagramme aus Texten erstellen, Klassen und Methoden mit Schleifen implementieren, Aufrufe mit Referenzen und Aliasen verfolgen, Reihungen von Objekten auswerten und Fehler im Quelltext finden. 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: Die ersten 15 Minuten sind kostenlos. Wer länger parkt, zahlt für die gesamte Parkdauer 150 Cent je angefangene halbe Stunde, 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
Aufrufe mit Referenzen verfolgen: Sportverein
AFB II

In der Mitgliederverwaltung eines Sportvereins senkt ermaessigen(prozent) den Jahresbeitrag (in Euro) mit der Anweisung beitrag = beitrag - beitrag * prozent / 100;

Mitglied a = new Mitglied("Ole", 120);
Mitglied b = new Mitglied("Ole", 120);
Mitglied c = a;
c.ermaessigen(25);
b = c;
b.ermaessigen(10);
int x = a.getBeitrag();
x = x - 1;
boolean gleich = (a == b);

Stellen Sie den Ablauf in einer Tracetabelle dar (Variablen und Objektzustände) und geben Sie die Endwerte an. Wie viele Mitglied-Objekte werden erzeugt, und welchen Wert hat gleich?

€
Ansatz: Nummeriere die Objekte (Objekt 1 aus Zeile 1, Objekt 2 aus Zeile 2) und notiere für a, b, c jeweils, auf welches Objekt sie zeigen.
Zeile 5: b = c kopiert die Referenz. Danach zeigen alle drei Variablen auf Objekt 1; Objekt 2 ist nicht mehr erreichbar.
Rechnen: 120 − 120 · 25 / 100 = 90; danach 90 − 90 · 10 / 100 = 81 (Ganzzahldivision).
Vollständige Lösung
Mitglied a = new Mitglied("Ole", 120);  // a -> O1 (120)
Mitglied b = new Mitglied("Ole", 120);  // b -> O2 (120)
Mitglied c = a;                         // c -> O1
c.ermaessigen(25);                      // O1: 90
b = c;                                  // b -> O1, O2 unerreichbar
b.ermaessigen(10);                      // O1: 81
int x = a.getBeitrag();                 // x = 81 (Kopie des Werts)
x = x - 1;                              // x = 80, O1 bleibt 81
boolean gleich = (a == b);              // true: gleiche Referenz

Endwerte: a.getBeitrag() = 81, x = 80, es wurden 2 Objekte erzeugt, gleich ist true. Hätte man a == b direkt nach Zeile 2 geprüft, wäre das Ergebnis false gewesen — trotz gleicher Attributwerte.