MINT lernen

Abituraufgaben: Vererbung

Zwei Abituraufgaben mit Hinweisen und Erwartungshorizont: überschreiben, super und Polymorphie — Onlineshop und Rollenspiel.

Dein Fortschritt:
0 / 0 Aufgaben
1

Der Onlineshop

AFB I–II

Ein Onlineshop verkauft Waren mit unterschiedlichen Steuersätzen. Die Preislogik wurde mit Vererbung modelliert.

Klassendiagramm des Onlineshops (Ausschnitt)
Artikel
  • - bezeichnung: Zeichenkette
  • # nettopreis: Fließkommazahl
  • c Artikel(bez: Zeichenkette, netto: Fließkommazahl)
  • + getPreis(): Fließkommazahl
Lebensmittel
  • - mhd: Zeichenkette
  • c Lebensmittel(bez: Zeichenkette, netto: Fließkommazahl, mhd: Zeichenkette)
  • + getPreis(): Fließkommazahl
Gutschein
    • c Gutschein(wert: Fließkommazahl)
    • + getPreis(): Fließkommazahl

    Ein Artikel kostet regulär den Nettopreis plus 19 % Mehrwertsteuer, Lebensmittel plus 7 %. Gutscheine sind steuerfrei. Der Warenkorb kennt beliebig viele Artikel (- artikel: DynArray<Artikel>) und berechnet mit gesamtpreis() die Summe aller Preise.

    1. Beschreiben Sie die im Klassendiagramm dargestellten Beziehungen. Gehen Sie auf die Bedeutung von # und auf das mehrfache Auftreten von getPreis() ein. 4 BE
    2. Implementieren Sie die Klasse Lebensmittel vollständig in Java. 5 BE
    3. Wenden Sie die Methode gesamtpreis() auf einen Warenkorb an, der einen Artikel("Kopfhörer", 50.00), ein Lebensmittel("Kaffee", 10.00, "2027-03-01") und einen Gutschein(20.00) enthält. Geben Sie für jeden Schleifendurchlauf die aufgerufene Methode und das Zwischenergebnis an. 4 BE
    4. Erläutern Sie, warum gesamtpreis() nicht geändert werden muss, wenn der Shop später eine Klasse Buch (ebenfalls 7 %) ergänzt. 4 BE

    Summe: 17 BE

    Hinweise

    Hinweis zu Aufgabe a)
    Das hohle Dreieck zeigt zur Oberklasse. Frage: Wer darf nettopreis lesen?
    Hinweis zu Aufgabe b)
    Kopf mit extends, Konstruktor mit super(…) als erster Anweisung, dann getPreis() überschreiben.
    Hinweis zu Aufgabe c)
    Welche getPreis() läuft, entscheidet das Objekt im jeweiligen Listenplatz.
    Hinweis zu Aufgabe d)
    Welchen Typ haben die Listenelemente, und welche Methode ruft die Schleife auf?

    Erwartungshorizont

    Erwartungshorizont zu Aufgabe a)

    Lebensmittel und Gutschein sind Unterklassen von Artikel (ein Lebensmittel ist ein Artikel). Sie erben bezeichnung, nettopreis und getPreis(); Lebensmittel ergänzt mhd. # bedeutet protected: nettopreis ist für die Unterklassen direkt zugreifbar, für andere Klassen nicht. getPreis() steht in allen drei Klassen: Die Unterklassen überschreiben die Methode der Oberklasse, weil sie mit anderem Steuersatz rechnen.

    Erwartungshorizont zu Aufgabe b)
    public class Lebensmittel extends Artikel {
        private String mhd;
    
        public Lebensmittel(String bezeichnung, double nettopreis, String mhd) {
            super(bezeichnung, nettopreis);
            this.mhd = mhd;
        }
        @Override
        public double getPreis() {
            return nettopreis * 1.07;
        }
    }

    Bewertet: extends (1 BE), Konstruktor mit super an erster Stelle (2 BE), überschreibende Methode mit gleichem Kopf (2 BE). @Override ist empfohlen, aber nicht bewertungsrelevant.

    Erwartungshorizont zu Aufgabe c)
    iObjektaufgerufenPreissumme
    0ArtikelArtikel.getPreis()59,5059,50
    1LebensmittelLebensmittel.getPreis()10,7070,20
    2GutscheinGutschein.getPreis()20,0090,20

    Rückgabe: 90,20 €. Obwohl die Liste den Typ Artikel hat, läuft jeweils die Methode der Klasse des Objekts (dynamische Bindung).

    Erwartungshorizont zu Aufgabe d)

    Die Schleife ruft für jedes Element nur getPreis() über den Typ Artikel auf. Ein Buch als Unterklasse von Artikel darf in die Liste eingefügt werden und bringt seine eigene überschreibende getPreis() mit; welche Methode läuft, wird erst zur Laufzeit am Objekt entschieden. Warenkorb muss die konkreten Unterklassen also gar nicht kennen — neue Artikelarten erweitern das Programm, ohne vorhandenen Code zu ändern (Polymorphie). Ohne Vererbung bräuchte man für jede Art eine eigene Liste oder eine Fallunterscheidung im Warenkorb.

    2

    Figuren im Rollenspiel

    AFB II–III

    In einem Rollenspiel kämpfen Figuren gegeneinander. Jede Figur hat Energie; treffen zieht dem Ziel so viel Energie ab, wie der Angreifer Schaden macht. Die Klassen liegen in einem gemeinsamen Paket.

    Klassen des Spiels (Java)
    public class Figur {
        private String name;
        protected int energie;
        public Figur(String name, int energie) { this.name = name; this.energie = energie; }
        public int schaden() { return 10; }
        public void treffen(Figur ziel) { ziel.energie = ziel.energie - schaden(); }
        public int getEnergie() { return energie; }
    }
    public class Ritter extends Figur {
        private int ruestung;
        public Ritter(String name, int ruestung) { super(name, 100); this.ruestung = ruestung; }
        @Override public int schaden() { return super.schaden() + 5; }
    }
    public class Magier extends Figur {
        public Magier(String name) { super(name, 60); }
        @Override public int schaden() { return 2 * super.schaden(); }
        @Override public void treffen(Figur ziel) { super.treffen(ziel); energie = energie - 5; }
    }
    1. Analysieren Sie den folgenden Ablauf. Geben Sie nach jeder Zeile die Energie von r und m an und begründen Sie jeweils, welche Methoden aufgerufen werden.
      Ritter r = new Ritter("Rolf", 3);
      Magier m = new Magier("Mia");
      Figur f = m;
      r.treffen(m);
      f.treffen(r);
      m.treffen(m);
      6 BE
    2. Entwerfen Sie eine Klasse Heiler, deren Objekte keinen Schaden anrichten, aber eine andere Figur um 15 Energiepunkte heilen können. Stellen Sie die Klasse im Klassendiagramm mit ihrer Beziehung zu Figur dar und implementieren Sie die neue Methode. 5 BE
    3. Implementieren Sie eine Methode static Figur staerkste(ArrayList<Figur> figuren), die die Figur mit der größten Energie liefert, bei leerer Liste null. 4 BE
    4. Beurteilen Sie den Vorschlag eines Teammitglieds, eine neue Klasse Drache als Unterklasse von Ritter zu modellieren, „weil ein Drache genauso viel Schaden macht und man so Code spart“. 4 BE

    Summe: 19 BE

    Hinweise

    Hinweis zu Aufgabe a)
    treffen ruft intern schaden() auf — und zwar die Methode des Objekts, an dem treffen läuft. Beim letzten Aufruf sind Angreifer und Ziel dasselbe Objekt.
    Hinweis zu Aufgabe b)
    Ein Heiler ist eine Figur. schaden() überschreiben, heilen(ziel: Figur) ergänzen; energie ist protected.
    Hinweis zu Aufgabe c)
    Muster „Maximum suchen“: Kandidat merken, alle Elemente vergleichen.
    Hinweis zu Aufgabe d)
    Wende den ist-ein-Test an und denk an das, was ein Drache alles erben würde.

    Erwartungshorizont

    Erwartungshorizont zu Aufgabe a)
    Zeiler.energiem.energieBegründung
    Konstruktoren10060Startwerte über super(name, …)
    r.treffen(m)10045geerbtes treffen, darin Ritter.schaden() = 10 + 5
    f.treffen(r)8040f zeigt auf den Magier: Magier.treffen → super.treffen mit Magier.schaden() = 20, danach verliert der Magier 5
    m.treffen(m)8015Ziel ist der Magier selbst: 40 − 20 = 20, dann − 5 = 15

    Entscheidend: f hat den Typ Figur, das Objekt ist aber ein Magier — es läuft seine überschreibende Methode. Innerhalb von Figur.treffen wird schaden() ebenfalls dynamisch gebunden.

    Erwartungshorizont zu Aufgabe b)
    Figur
    • - name: Zeichenkette
    • # energie: Ganzzahl
    • + schaden(): Ganzzahl
    • + treffen(ziel: Figur)
    Heiler
      • c Heiler(name: Zeichenkette)
      • + schaden(): Ganzzahl
      • + heilen(ziel: Figur)
      public class Heiler extends Figur {
          public Heiler(String name) {
              super(name, 70);
          }
          @Override
          public int schaden() {
              return 0;
          }
          public void heilen(Figur ziel) {
              ziel.energie = ziel.energie + 15;
          }
      }

      Der Startwert 70 ist frei gewählt. Weil energie protected ist und alle Klassen im selben Paket liegen, darf heilen die Energie des Ziels ändern; sauberer wäre eine Methode erhoeheEnergie(n: Ganzzahl) in Figur.

      Erwartungshorizont zu Aufgabe c)
      static Figur staerkste(ArrayList<Figur> figuren) {
          Figur beste = null;
          for (int i = 0; i < figuren.size(); i++) {
              if (beste == null || figuren.get(i).getEnergie() > beste.getEnergie()) {
                  beste = figuren.get(i);
              }
          }
          return beste;
      }

      Für die Liste aus a) (Magier, Ritter) liefert die Methode den Ritter. Die Bedingung beste == null behandelt das erste Element; bei leerer Liste bleibt beste gleich null.

      Erwartungshorizont zu Aufgabe d)

      Der Vorschlag ist abzulehnen. „Ein Drache ist ein Ritter“ ist fachlich falsch; der Drache würde auch ruestung und alles, was Ritter künftig bekommt (z. B. ein Pferd), erben. Ändert jemand den Ritter-Schaden, ändert sich unbeabsichtigt auch der Drache. Gleicher Schaden ist kein Grund für Vererbung, sondern Zufall der aktuellen Werte. Besser: Drache als eigene Unterklasse von Figur mit eigener schaden()-Methode; gemeinsamer Code gehört in die gemeinsame Oberklasse Figur.