MINT lernen

Abituraufgaben: Assoziationen zwischen Klassen

Zwei Abituraufgaben mit Hinweisen und Erwartungshorizont: Kardinalitäten umsetzen, Beziehungen in beide Richtungen pflegen — Paketstation und Platzreservierung.

Dein Fortschritt:
0 / 0 Aufgaben
1

Die Paketstation

AFB I–II

Ein Logistikunternehmen betreibt Paketstationen mit je 24 Fächern. Jedes Paket ist für genau einen Kunden bestimmt. Ein Kunde kann mehrere Pakete in derselben Station liegen haben. Die Software ist bereits in Teilen modelliert.

Klassendiagramm der Paketstation (Ausschnitt)
Paketstation
  • - standort: Zeichenkette
  • - faecher: Reihung von Paket
  • c Paketstation(standort: Zeichenkette)
  • + einlegen(fach: Ganzzahl, p: Paket)
  • + abholen(k: Kunde): Ganzzahl
faecher0..24
Paket
  • - sendungsnummer: Zeichenkette
  • - empfaenger: Kunde
  • c Paket(nr: Zeichenkette, k: Kunde)
  • + getEmpfaenger(): Kunde
empfaenger1
Kunde
  • - name: Zeichenkette
  • c Kunde(name: Zeichenkette)
  • + getName(): Zeichenkette
  1. Beschreiben Sie die im Klassendiagramm dargestellten Beziehungen. Gehen Sie dabei auf Navigationsrichtung und Kardinalitäten ein. 4 BE
  2. Implementieren Sie die Methode abholen(k: Kunde): Ganzzahl der Klasse Paketstation. Sie entnimmt alle Pakete, deren Empfänger k ist (die Fächer werden frei), und liefert die Anzahl der entnommenen Pakete. 6 BE
  3. Stellen Sie den Zustand nach den folgenden Anweisungen in einem Objektdiagramm dar. Beschränken Sie sich bei der Station auf die belegten Fächer.
    Kunde ina = new Kunde("Ina");
    Kunde tom = new Kunde("Tom");
    Paketstation st = new Paketstation("Bahnhof");
    st.einlegen(0, new Paket("A1", ina));
    st.einlegen(3, new Paket("B7", tom));
    st.einlegen(5, new Paket("C2", ina));
    Geben Sie außerdem die Rückgabewerte von st.abholen(ina) an, wenn diese Methode zweimal hintereinander aufgerufen wird. 5 BE
  4. Erläutern Sie, welche Änderungen nötig sind, wenn ein Kunde jederzeit alle für ihn eingelagerten Pakete kennen soll, und welches neue Risiko dadurch entsteht. 4 BE

Summe: 19 BE

Hinweise

Hinweis zu Aufgabe a)
Lies jede Linie in Pfeilrichtung als Satz und nenne die Zahl am Linienende.
Hinweis zu Aufgabe b)
Zählschleife über alle Fächer. Leere Fächer enthalten null — erst prüfen, dann getEmpfaenger() aufrufen. Objekte vergleicht man hier mit ==: gemeint ist genau dieses Kunden-Objekt.
Hinweis zu Aufgabe c)
Objektkarten mit unterstrichenem Kopf, Referenzen als Pfeile. Zwei Pakete zeigen auf dasselbe Kunden-Objekt.
Hinweis zu Aufgabe d)
Überlege, wo zusätzlich ein Attribut nötig ist und wer es beim Einlagern und Abholen pflegen muss.

Erwartungshorizont

Erwartungshorizont zu Aufgabe a)

Eine Paketstation kennt über die Rolle faecher höchstens 24 Pakete (0..24, also auch keins); umgesetzt als Reihung der Länge 24, freie Fächer sind null. Ein Paket kennt über empfaenger genau einen Kunden (1, nie null, im Konstruktor gesetzt). Beide Assoziationen sind nur in Pfeilrichtung navigierbar: Ein Paket weiß nicht, in welcher Station es liegt, ein Kunde kennt seine Pakete nicht.

Erwartungshorizont zu Aufgabe b)
public int abholen(Kunde k) {
    int anzahl = 0;
    for (int i = 0; i < faecher.length; i++) {
        if (faecher[i] != null && faecher[i].getEmpfaenger() == k) {
            faecher[i] = null;
            anzahl++;
        }
    }
    return anzahl;
}

Bewertet: Durchlauf über alle 24 Fächer (2 BE), null-Prüfung vor dem Aufruf mit Kurzschlussauswertung (1 BE), Vergleich des Empfängers (1 BE), Fach freigeben und zählen (1 BE), Rückgabe (1 BE). Ein vorzeitiges return beim ersten Treffer ist falsch, weil ein Kunde mehrere Pakete haben kann.

Erwartungshorizont zu Aufgabe c)
st: Paketstation
  • standort = "Bahnhof"
  • faecher[0] → p1
  • faecher[3] → p2
  • faecher[5] → p3
p1: Paket
  • sendungsnummer = "A1"
  • empfaenger → ina
p2: Paket
  • sendungsnummer = "B7"
  • empfaenger → tom
p3: Paket
  • sendungsnummer = "C2"
  • empfaenger → ina
ina: Kunde
  • name = "Ina"
tom: Kunde
  • name = "Tom"

Die Pakete haben keinen Variablennamen im Programm; die Namen p1–p3 dienen nur dem Diagramm. Zwei Pakete verweisen auf dasselbe Objekt ina. Rückgabewerte: erster Aufruf 2 (Fächer 0 und 5 werden frei), zweiter Aufruf 0.

Erwartungshorizont zu Aufgabe d)

Kunde bekommt ein Attribut - pakete: DynArray<Paket> (Kardinalität * an der Paket-Seite), die Assoziation wird in beide Richtungen navigierbar. einlegen muss das Paket zusätzlich beim Empfänger eintragen, abholen es dort wieder entfernen. Risiko: Die Informationen liegen jetzt doppelt vor. Vergisst eine Methode eine der beiden Seiten, widersprechen sich Station und Kunde (Paket gilt beim Kunden als eingelagert, ist aber längst abgeholt). Deshalb sollten nur wenige, gut getestete Methoden beide Seiten gemeinsam ändern.

2

Sitzplatzreservierung im Zug

AFB II–III

Eine Bahngesellschaft lässt ihre Platzreservierung neu programmieren. Die Anforderungen stehen im Kasten.

Anforderungen der Bahngesellschaft

(1) Ein Zug besteht aus mindestens einem Wagen; Wagen können angehängt werden. (2) Jeder Wagen hat eine Nummer und genau 60 Sitzplätze. (3) Ein Sitzplatz ist frei oder genau einer Reservierung zugeordnet. (4) Eine Reservierung kennt den Namen der reisenden Person und ihren Sitzplatz. (5) Der Zug soll die Zahl seiner freien Sitzplätze angeben können.

  1. Erstellen Sie ein Klassendiagramm mit den Klassen Zug, Wagen, Sitzplatz und Reservierung, das die Anforderungen (1) bis (5) erfüllt. Geben Sie Navigationsrichtungen und Kardinalitäten an. 6 BE
  2. Implementieren Sie die Methode freiePlaetze(): Ganzzahl der Klasse Zug. Gehen Sie davon aus, dass jeder Wagen im Konstruktor alle 60 Sitzplatz-Objekte erzeugt und die Methoden getSitz(i: Ganzzahl): Sitzplatz und getAnzahlSitze(): Ganzzahl anbietet. 5 BE
  3. Analysieren Sie die folgende Methode aus der Klasse Zug, mit der ein Mitarbeiter eine Reservierung anlegt. Zeigen Sie an einem Ablauf, welches Problem sie verursachen kann.
    public void reservieren(String name, Wagen w, int i) {
        Sitzplatz s = w.getSitz(i);
        Reservierung r = new Reservierung(name, s);
    }
    Dabei sei Reservierung so implementiert, dass der Konstruktor nur this.sitz = sitz; ausführt. 4 BE
  4. Beurteilen Sie den Vorschlag, in Sitzplatz statt der Referenz auf eine Reservierung nur ein Attribut - belegt: Wahrheitswert zu speichern. 4 BE

Summe: 19 BE

Hinweise

Hinweis zu Aufgabe a)
Gehe Satz für Satz vor: Welche Mengenwörter stecken in (1) bis (4)? Bei (3) und (4) kennen sich beide Seiten.
Hinweis zu Aufgabe b)
Zwei geschachtelte Schleifen: außen über die ArrayList der Wagen, innen über die Sitze eines Wagens.
Hinweis zu Aufgabe c)
Frag nach dem Aufruf: Was weiß die Reservierung, und was weiß der Sitzplatz?
Hinweis zu Aufgabe d)
Welche Information aus Anforderung (4) und (5) wäre mit einem Wahrheitswert noch verfügbar?

Erwartungshorizont

Erwartungshorizont zu Aufgabe a)
Zug
  • - wagen: DynArray<Wagen>
  • c Zug(erster: Wagen)
  • + anhaengen(w: Wagen)
  • + freiePlaetze(): Ganzzahl
wagen1..*
Wagen
  • - nummer: Ganzzahl
  • - sitze: Reihung von Sitzplatz
  • c Wagen(nummer: Ganzzahl)
  • + getSitz(i: Ganzzahl): Sitzplatz
  • + getAnzahlSitze(): Ganzzahl
sitze60
Sitzplatz
  • - nummer: Ganzzahl
  • - reservierung: Reservierung
  • + istFrei(): Wahrheitswert
  • + setReservierung(r: Reservierung)
reservierung / sitz10..1
Reservierung
  • - reisender: Zeichenkette
  • - sitz: Sitzplatz
  • c Reservierung(name: Zeichenkette, s: Sitzplatz)

(1) „mindestens einem Wagen“ → 1..*, umgesetzt als dynamische Reihung; (2) „genau 60“ → Kardinalität 60, umgesetzt als Reihung der Länge 60; (3) „frei oder genau einer“ → 0..1 an der Reservierungs-Seite; (4) „kennt ihren Sitzplatz“ → 1 an der Sitzplatz-Seite. Weil Sitzplatz und Reservierung sich gegenseitig kennen müssen, ist diese Assoziation in beide Richtungen navigierbar (Linie ohne Pfeilspitzen). Je 1 BE für jede der vier Beziehungen, 2 BE für sinnvolle Attribute und Methoden.

Erwartungshorizont zu Aufgabe b)
public int freiePlaetze() {
    int frei = 0;
    for (int w = 0; w < wagen.size(); w++) {
        Wagen wg = wagen.get(w);
        for (int i = 0; i < wg.getAnzahlSitze(); i++) {
            if (wg.getSitz(i).istFrei()) {
                frei++;
            }
        }
    }
    return frei;
}

Test: Ein Zug mit zwei Wagen und zwei Reservierungen liefert 118. Bewertet: äußere Schleife mit size()/get (2 BE), innere Schleife (1 BE), Prüfung und Zählen (1 BE), Rückgabe (1 BE).

Erwartungshorizont zu Aufgabe c)

Nach dem Aufruf kennt r den Sitzplatz, der Sitzplatz aber nicht die Reservierung: s.istFrei() liefert weiterhin true. Ablauf: reservieren("Lea", w2, 10), danach reservieren("Max", w2, 10) — beide Aufrufe gelingen, der Platz ist doppelt vergeben, und freiePlaetze() zählt ihn trotzdem als frei. Ursache: Bei einer Assoziation in beide Richtungen wurde nur eine Seite gesetzt. Abhilfe: Im Konstruktor der Reservierung sitz.setReservierung(this); ergänzen und in reservieren vorher s.istFrei() prüfen.

Erwartungshorizont zu Aufgabe d)

Vorteil: einfacher, keine gegenseitigen Referenzen, die konsistent gehalten werden müssen; Anforderung (5) lässt sich damit weiterhin erfüllen. Nachteil: Vom Sitzplatz aus ist nicht mehr feststellbar, wer dort sitzt; beim Stornieren muss man die Reservierung über den Namen suchen, und belegt kann dem Bestand der Reservierungen widersprechen (Datenredundanz auf andere Art). Urteil: Für eine reine Belegungsanzeige genügt der Wahrheitswert; da eine Bahn aber Platzkarten prüfen und stornieren muss, ist die Referenz auf die Reservierung die bessere Modellierung — die Konsistenz sichert man durch eine einzige Methode, die beide Seiten setzt.