Übungsaufgaben zu Vererbung, abstrakten Klassen und Interfaces

https://bildung.social/@oerinformatik/

https://oer-informatik.de/uml-klassendiagramm-vererbung-uebungsaufgaben

UML-Klassendiagramm, Vererbung, abstrakte Klasse, Interface, Realisierung, Sichtbarkeit, Polymorphie, Pseudocode, Übungsaufgaben, Prüfungsvorbereitung

tl/dr; (Bearbeitungszeit insgesamt 80 min, mehrere Aufgaben): Drei Übungsaufgaben zu Vererbung, abstrakten Klassen und Interfaces: eine Klassenhierarchie aus doppeltem Quelltext entwickeln, einen fehlerhaften Entwurf mit Interface korrigieren und um eine abstrakte Klasse erweitern sowie Verhalten über ein Interface austauschbar machen. Alle Lösungen sind ausgeblendet und mit Punkteverteilung versehen [AI-Assisted]. (Zuletzt geändert am 29.10.2026)

Dieser Artikel ist Teil einer Artikelreihe über UML-Klassendiagramme: Einstieg in das Klassendiagramm (Übung Ticketautomat, Übungsaufgaben) / Sichtbarkeitsmodifikatoren / Assoziation, Aggregation, Komposition (Übungsaufgaben) / Vererbung (Übungsaufgaben) / Abstrakte Klassen und Interfaces / Polymorphie (Übungsaufgaben). Dazu: Klassendiagramme mit PlantUML zeichnen und Objektdiagramm.

Diese Übungsaufgaben gehören zu den Artikeln Klassenbeziehungen: Vererbung von Klassen in der UML und Abstrakte Klassen und Interfaces. Sie bauen auf den Grundlagen des UML-Klassendiagramms und den Sichtbarkeitsmodifikatoren auf.

Wortwolke mit den Begriffen der Übungsaufgaben: Vererbung, Interface, abstrakte Klasse, Realisierung, Oberklasse, Unterklasse, Überschreiben (mit KI erstellt)

Hinweise zur Bearbeitung: Als FIA oder FIDP musst Du zur Teil-2-Prüfung Aufgaben auf Kernkompetenz-Ebene lösen können, das sind Aufgabe 2.1, Aufgabe 2.2 und Aufgabe 2.3. Zeichne Deine Klassendiagramme auf Papier oder mit einem Werkzeug Deiner Wahl, bevor Du die Beispiellösung aufklappst. Gib Attribute und Methoden mit vollständiger Signatur an: Sichtbarkeit (+ public, - private, # protected), Name, Parameter mit Datentyp und Rückgabetyp. Als Datentypen sind UML-Typen (Integer, Real, Boolean, String) ebenso richtig wie die Typen einer Programmiersprache (int, double, boolean). Abstrakte Klassen und Methoden kennzeichnest Du handschriftlich am sichersten mit {abstract}. Pseudocode muss keiner bestimmten Sprache folgen, aber erkennbar mit Objekten und Methodenaufrufen arbeiten. Andere fachlich richtige Lösungen sind ebenfalls richtig.

Du möchtest eigene Aufgaben zum Üben von einem KI-Sprachmodell generieren lassen?

Kopiere den Text in ein KI-Sprachmodell Deiner Wahl und passe Kompetenzstufe, Anzahl, Bearbeitungszeit und Szenario bei Bedarf an. Wie die Aufgaben aufgebaut sind, beschreibt der Artikel Wie sind die Aufgaben aufgebaut?. Prüfe die Ergebnisse mit dem Lehrartikel: KI-Sprachmodelle machen Fehler.

Erstelle neue Übungsaufgaben zum Thema „Vererbung, abstrakten Klassen
und Interfaces“.

Bauplan: Halte Dich an Aufbau, Kompetenzstufen, Operatoren,
Anforderungsbereiche, Punkte und Zeitschätzung, die im Artikel
https://oer-informatik.de/aufgaben-aufbau beschrieben sind.

Fachliche Grundlage:
- https://oer-informatik.de/uml-klassendiagramm-vererbung
- https://oer-informatik.de/uml-klassendiagramm-interface-abstraktion
- https://oer-informatik.de/uml-klassendiagramm
- https://oer-informatik.de/uml-klassendiagramm-sichtbarkeitsmodifikatoren
- https://oer-informatik.de/uml-klassendiagramm-polymorphie
- https://oer-informatik.de/uml-klassendiagramm-assoziation
- https://oer-informatik.de/uml-klassendiagramm-plantuml
- Stilbeispiel: https://oer-informatik.de/uml-klassendiagramm-vererbung-uebungsaufgaben

Die Aufgaben sollen Inhalte zu diesen Schlagwörtern abdecken:
- UML-Klassendiagramm
- Vererbung
- abstrakte Klasse
- Interface
- Realisierung
- Sichtbarkeit
- Polymorphie
- Pseudocode

Kompetenzstufe: Kernkompetenz (alternativ: Basiskompetenz, Profilkompetenz, Expertise)
Anzahl: 1 Aufgabe mit Teilaufgaben
Bearbeitungszeit in Minuten ca.: <5 bis 30 min>
Szenario: eines der Szenarien aus dem Bauplan

Vorgehen:
1. Lies zuerst die verlinkten Artikel. Kannst Du keine Webseiten
   abrufen, sag mir das, statt zu raten.
2. Steht bei einer Angabe noch ein Platzhalter in <…>, frag mich
   zuerst danach. Richte Punkte und Zahl der Teilaufgaben nach der
   Bearbeitungszeit (ca. 0,9 min je Punkt).
3. Gib je Aufgabe Titel mit Kompetenzstufe, Ausgangssituation und
   Teilaufgaben mit Operator und Punktzahl aus, dazu die geschätzte
   Bearbeitungszeit.
4. Gib die Beispiellösungen mit Punkteverteilung erst aus, wenn ich
   danach frage.

Aufgabe 2.1: Fahrradtypen im Konfigurator (Kernkompetenz)

(ca. 28 min Bearbeitungszeit)

Du bist Auszubildende*r im Entwicklungsteam der CSB-Rad GmbH in Bielefeld. Das Backend des Fahrradkonfigurators ist über die Jahre gewachsen: Für Stadträder und Lastenräder gibt es zwei unabhängige Java-Klassen. Ab Frühjahr 2027 sollen auch E-Bikes konfiguriert werden können. Cem Sarıkaya, Geschäftsführer Technik und IT, möchte vorher aufräumen: „Bevor wir eine dritte Kopie anlegen, bauen wir eine saubere Klassenhierarchie.“

Auszug aus dem vorhandenen Quelltext (Getter, Setter und weitere Methoden ausgelassen):

public class Stadtrad {
    private String artikelnummer;
    private String bezeichnung;
    private double basispreis;
    private int rahmenhoehe;          // in cm
    private int gangzahl;

    public Stadtrad(String artikelnummer, String bezeichnung, double basispreis,
                    int rahmenhoehe, int gangzahl) {
        this.artikelnummer = artikelnummer;
        this.bezeichnung = bezeichnung;
        this.basispreis = basispreis;
        this.rahmenhoehe = rahmenhoehe;
        this.gangzahl = gangzahl;
    }

    public String getBezeichnung() { return bezeichnung; }

    public double berechnePreis() {
        if (gangzahl > 8) {
            return basispreis + 120.00;   // Aufpreis Kettenschaltung
        }
        return basispreis;
    }

    public double berechneVersandkosten() { return 49.00; }  // Spedition
}

public class Lastenrad {
    private String artikelnummer;
    private String bezeichnung;
    private double basispreis;
    private int rahmenhoehe;          // in cm
    private int zuladung;             // in kg
    private boolean regenverdeck;

    public Lastenrad(String artikelnummer, String bezeichnung, double basispreis,
                     int rahmenhoehe, int zuladung, boolean regenverdeck) {
        this.artikelnummer = artikelnummer;
        this.bezeichnung = bezeichnung;
        this.basispreis = basispreis;
        this.rahmenhoehe = rahmenhoehe;
        this.zuladung = zuladung;
        this.regenverdeck = regenverdeck;
    }

    public String getBezeichnung() { return bezeichnung; }

    public double berechnePreis() {
        if (regenverdeck) {
            return basispreis + 289.00;
        }
        return basispreis;
    }

    public double berechneVersandkosten() { return 49.00 + 35.00; }  // Spedition + Sperrgutzuschlag
}

Vorgaben für E-Bikes: Ein E-Bike hat zusätzlich die Akkukapazität in Wattstunden (ganze Zahl). Der Preis ist der Basispreis; ein Akku mit mehr als 500 Wh kostet 180,00 € Aufpreis. Versendet wird wie ein Stadtrad per Spedition.

  1. Beschreibe, was man in der objektorientierten Programmierung unter Vererbung versteht. Nenne zwei Stellen im Quelltext, an denen sich zeigt, dass die beiden Klassen von einer gemeinsamen Oberklasse profitieren würden. (4 Punkte)

Beschreibung (2 Punkte): Bei der Vererbung wird eine spezialisierte Klasse (Unterklasse, Subklasse, abgeleitete Klasse) von einer allgemeineren Klasse (Oberklasse, Superklasse, Basisklasse) abgeleitet. Die Unterklasse übernimmt alle Attribute und Methoden der Oberklasse, ohne sie selbst noch einmal implementieren zu müssen, und kann eigene Attribute und Methoden ergänzen oder geerbte Methoden überschreiben. Zwischen Unter- und Oberklasse besteht eine „ist-ein“-Beziehung (ein Lastenrad ist ein Fahrrad).

Stellen im Quelltext (2 × 1 Punkt), z. B.:

  • Die Attribute artikelnummer, bezeichnung, basispreis und rahmenhoehe sind in beiden Klassen identisch deklariert.

  • Die Zuweisungen dieser vier Attribute in den Konstruktoren sind identisch.

  • getBezeichnung() ist in beiden Klassen gleich implementiert.

  • Beide Klassen haben die Methoden berechnePreis() und berechneVersandkosten() mit gleicher Signatur, aber unterschiedlicher Berechnung.

  1. Entwickle ein UML-Klassendiagramm mit einer Oberklasse Fahrrad und den Klassen Stadtrad, Lastenrad und EBike.
  • Gemeinsame Attribute und Methoden stehen nur einmal in der Oberklasse.

  • Es sollen keine Objekte der Klasse Fahrrad erzeugt werden können. Jede Fahrradart muss ihre eigene Preisberechnung haben.

  • Die Versandkosten per Spedition werden zentral berechnet; abweichende Versandkosten werden in der jeweiligen Klasse festgelegt.

  • Die Unterklassen sollen den Basispreis für ihre Preisberechnung direkt lesen können; alle übrigen Attribute bleiben gekapselt.

  • Gib alle Attribute, Konstruktoren und Methoden mit vollständiger Signatur und Sichtbarkeit an. Getter und Setter außer getBezeichnung() musst Du nicht eintragen.

(10 Punkte)

Lösung: abstrakte Oberklasse Fahrrad mit den Unterklassen Stadtrad, Lastenrad und EBike (mit KI erstellt)

Bewertung (10 Punkte):

  • je 1 Punkt für die vier Klassen mit Namen (4 Punkte)

  • Fahrrad als abstrakt gekennzeichnet ({abstract}, kursiv oder <<abstract>>) (1 Punkt)

  • berechnePreis(): Real in Fahrrad als abstrakte Methode (1 Punkt)

  • gemeinsame Attribute, basispreis als protected (#), Konstruktor und getBezeichnung() nur in Fahrrad; berechneVersandkosten() als konkrete Methode in Fahrrad (1 Punkt)

  • Unterklassen mit eigenen Attributen und eigenem Konstruktor (alle Parameter, auch die für die Oberklasse) (1 Punkt)

  • überschriebene Methoden in den Unterklassen: berechnePreis() in allen drei, berechneVersandkosten() nur in Lastenrad (1 Punkt)

  • drei Vererbungsbeziehungen mit hohlem Dreieck an Fahrrad (einzeln oder als Sammelpfeil) (1 Punkt)

Typische Fehler: geerbte Attribute in den Unterklassen wiederholen; berechneVersandkosten() in Stadtrad und EBike noch einmal eintragen, obwohl sie dort nicht anders berechnet wird; den Pfeil an der Unterklasse ansetzen; Konstruktoren in der Oberklasse für vererbt halten (Konstruktoren werden nicht vererbt, jede Unterklasse braucht einen eigenen).

  1. Das Team probiert den neuen Entwurf mit einer weiteren Fahrradart aus, einem Faltrad. Beim Übersetzen bricht der Java-Compiler mit zwei Fehlern ab:
public class Faltrad extends Fahrrad {
    private int faltmass;   // Packmaß in cm

    public Faltrad(String artikelnummer, String bezeichnung, double basispreis,
                   int rahmenhoehe, int faltmass) {
        super(artikelnummer, bezeichnung, basispreis, rahmenhoehe);
        this.faltmass = faltmass;
    }
}

public class Konfiguratortest {

    public static void main(String[] args) {
        Fahrrad falt = new Faltrad("CSB-3105", "Teuto Fold", 749.00, 44, 78);
        Fahrrad probe = new Fahrrad("CSB-0001", "Testrad", 999.00, 52);
        System.out.println(falt.berechnePreis() + probe.berechnePreis());
    }
}
Faltrad.java:1: error: Faltrad is not abstract and does not override abstract method berechnePreis() in Fahrrad
Konfiguratortest.java:5: error: Fahrrad is abstract; cannot be instantiated

Erkläre für beide Meldungen, gegen welche Festlegung aus Deinem Klassendiagramm der Quelltext verstößt, und gib jeweils eine Korrektur an. Begründe außerdem, warum es für den Shop ein Vorteil ist, dass der Compiler hier so streng ist. (5 Punkte)

Je Meldung 1 Punkt für die Erklärung und 1 Punkt für die Korrektur, 1 Punkt für die Begründung:

  • Faltrad is not abstract and does not override abstract method berechnePreis(): berechnePreis() ist in Fahrrad abstrakt, steht dort also nur als Signatur ohne Rumpf. Jede Unterklasse, von der es Objekte geben soll, muss die Methode überschreiben; Faltrad tut das nicht. Korrektur: in Faltrad die Methode ergänzen, z. B. @Override public double berechnePreis() { return basispreis; }. (Die Klasse selbst abstrakt zu machen, wäre zwar übersetzbar, dann ließen sich aber keine Falträder anlegen.)

  • Fahrrad is abstract; cannot be instantiated: Fahrrad ist als abstrakte Klasse modelliert. Sie ist nur die gemeinsame Vorlage, von ihr selbst lassen sich keine Objekte erzeugen; im Shop gibt es kein „Fahrrad an sich“. Korrektur: ein Objekt einer konkreten Unterklasse erzeugen, z. B. Fahrrad probe = new EBike("CSB-2207", "Teuto E-City", 2899.00, 50, 625);. Der Variablentyp darf Fahrrad bleiben.

  • Vorteil: Eine Klasse mit abstrakter Methode muss selbst abstrakt sein, und nur vollständige Unterklassen werden konkret. So steht schon beim Übersetzen fest, dass sich für jedes Fahrradobjekt im Shop ein Preis berechnen lässt. Eine vergessene Preisberechnung fällt nicht erst auf, wenn eine Kundin bestellt.

Mit beiden Korrekturen übersetzt das Programm und gibt 3828.0 aus (749,00 € für das Faltrad, 2899,00 € + 180,00 € Akkuaufpreis für das E-Bike).

  1. Begründe, warum das Attribut basispreis mit der Sichtbarkeit protected statt private modelliert wird. (2 Punkte)

Auf private-Attribute kann nur die Klasse selbst zugreifen, nicht ihre Unterklassen. Die Unterklassen brauchen den Basispreis aber in ihrer eigenen Implementierung von berechnePreis(). Mit protected (#) dürfen die Klasse und alle Unterklassen zugreifen, andere Klassen weiterhin nicht – die Kapselung nach außen bleibt erhalten (2 Punkte).

Ebenfalls richtig (mit Begründung): basispreis bleibt private und Fahrrad bietet einen Getter getBasispreis() an, der protected oder public ist.

  1. Implementiere in Pseudocode den Konstruktor der Klasse Lastenrad. (4 Punkte)
öffentlich Lastenrad(artikelnummer: String, bezeichnung: String, basispreis: Real,
                     rahmenhoehe: Integer, zuladung: Integer, regenverdeck: Boolean)
    Basisklassenkonstruktor Fahrrad(artikelnummer, bezeichnung, basispreis, rahmenhoehe) aufrufen
    this.zuladung := zuladung
    this.regenverdeck := regenverdeck
Ende Lastenrad

In Java:

public Lastenrad(String artikelnummer, String bezeichnung, double basispreis,
                 int rahmenhoehe, int zuladung, boolean regenverdeck) {
    super(artikelnummer, bezeichnung, basispreis, rahmenhoehe);
    this.zuladung = zuladung;
    this.regenverdeck = regenverdeck;
}

Bewertung: Methodenkopf mit allen Parametern (1 Punkt), Aufruf des Basisklassenkonstruktors mit den vier gemeinsamen Werten (2 Punkte), Setzen der eigenen Attribute (1 Punkt). Werden die geerbten Attribute direkt zugewiesen statt über den Basisklassenkonstruktor, gibt es für diesen Teil keine Punkte: Bis auf basispreis (protected) sind sie in Fahrrad private und für Lastenrad nicht zugreifbar; auch basispreis gehört in den Basisklassenkonstruktor.

  1. Implementiere in Pseudocode die Methode berechneVersandkosten() der Klasse Lastenrad. Nutze dabei die Implementierung der Oberklasse, damit eine Änderung der Speditionskosten nur an einer Stelle nötig ist. (3 Punkte)
öffentlich berechneVersandkosten(): Real
    Rückgabe berechneVersandkosten() der Basisklasse + 35,00
Ende berechneVersandkosten

In Java: return super.berechneVersandkosten() + 35.00; (mit @Override vor der Methode).

Bewertung: Methodenkopf mit gleicher Signatur wie in Fahrrad (1 Punkt), Aufruf der Basisklassenmethode (1 Punkt), Zuschlag und Rückgabe (1 Punkt). Wer 49,00 + 35,00 fest einträgt, erhält 1 Punkt: Das Ergebnis stimmt, aber eine Preisänderung bei der Spedition müsste dann an zwei Stellen gepflegt werden.

  1. Ab 2027 verkauft die CSB-Rad GmbH auch Kinderräder. Zusätzlich zu den gemeinsamen Angaben wird die Laufradgröße in Zoll (ganze Zahl) gespeichert. Der Preis ist immer der Basispreis, verschickt wird per Paket für 6,90 €.

Ergänze das Klassendiagramm aus b) um die erforderliche Klasse. Begründe, warum die vorhandenen Klassen dafür nicht geändert werden müssen. (3 Punkte)

Lösung: Klasse Kinderrad als weitere Unterklasse von Fahrrad (mit KI erstellt)

Ergänzung (2 Punkte): Klasse Kinderrad mit -laufradgroesse: Integer, eigenem Konstruktor, den überschriebenen Methoden berechnePreis(): Real (Pflicht, da abstrakt) und berechneVersandkosten(): Real (abweichende Versandkosten) sowie der Vererbungsbeziehung zu Fahrrad (1 Punkt Klasse, 1 Punkt Methoden und Beziehung).

Begründung (1 Punkt): Alle gemeinsamen Eigenschaften erbt Kinderrad von Fahrrad; das Abweichende wird nur in der neuen Klasse festgelegt. Die Oberklasse und die anderen Unterklassen kennen ihre Unterklassen nicht und bleiben unverändert – die Hierarchie ist für neue Fahrradarten erweiterbar, ohne vorhandenen, getesteten Code anzufassen.

Aufgabe 2.2: Anzeigen für den Sessionplan (Kernkompetenz)

(ca. 30 min Bearbeitungszeit)

Du bist Auszubildende*r und arbeitest im Orga-Team des Barcamps „Lernen.Digital.Leben 2027“ am Ratz und Rübe Berufskolleg in Bielefeld mit. Am Veranstaltungstag wird der Sessionplan morgens im Plenum ergänzt und im Laufe des Tages oft geändert. Jede Änderung soll sofort auf den E-Ink-Türschildern vor den Räumen und auf den Infomonitoren in Foyer und Mensa erscheinen. Emre Aksoy, Leitung des Technikteams, hat am Vorabend einen ersten Entwurf gezeichnet. Bevor Dimitrios Papadopoulos, Gesamtleitung der Orga, ihn freigibt, sollst Du ihn durchsehen:

Erster Entwurf mit Sessionplan, dem Interface Anzeige sowie den Klassen Tuerschild und Infomonitor, der noch Modellierungsfehler enthält (mit KI erstellt)

Was der Entwurf leisten soll:

  • Jede Anzeige meldet sich beim Start mit anmelden() beim Sessionplan an. Der Sessionplan soll mit jeder Art von Anzeige zusammenarbeiten, auch mit Anzeigearten, die erst später dazukommen.

  • sessionVerschieben() ändert Raum und Slot einer Session und ruft danach alleAnzeigenAktualisieren() auf.

  • aktualisieren(plan) holt sich über plan.getSessionsFuerRaum() die Daten und zeigt sie an. istErreichbar() prüft, ob das Gerät gerade im Netz erreichbar ist. Türschilder und Infomonitore brauchen beide Methoden.

  1. Emre sind beim Zeichnen vier Fehler unterlaufen. Finde sie, erkläre jeweils, welche Regel der UML oder welche Anforderung verletzt ist, und skizziere den korrigierten Entwurf. (8 Punkte)
Lösung: korrigierter Entwurf mit Realisierungen zum Interface Anzeige, Aggregation vom Sessionplan zu Anzeige und vollständigen Methoden (mit KI erstellt)

Je Fehler 1 Punkt für die Erklärung und 1 Punkt für die Korrektur in der Skizze:

  1. Durchgezogene Linie von Tuerschild zu Anzeige: Das ist die Notation der Vererbung. Ein Interface wird realisiert, nicht beerbt; richtig ist eine gestrichelte Linie mit hohlem Dreieck am Interface (in Java implements statt extends).

  2. aktualisieren() ohne Parameter in Tuerschild: Die Signatur passt nicht zum Interface. So entstünde eine zusätzliche, überladene Methode, während aktualisieren(plan: Sessionplan) aus dem Interface unimplementiert bliebe. Korrektur: +aktualisieren(plan: Sessionplan).

  3. istErreichbar() fehlt in Infomonitor: Eine Klasse, die ein Interface realisiert, muss alle seine Methoden implementieren. Sonst bleibt sie abstrakt, und es ließen sich keine Infomonitor-Objekte erzeugen. Korrektur: +istErreichbar(): Boolean ergänzen.

  4. Aggregationen vom Sessionplan zu den konkreten Klassen: Das Attribut anzeigen hat den Typ Anzeige, und der Sessionplan soll mit jeder Anzeigeart arbeiten. Mit Beziehungen zu Tuerschild und Infomonitor müsste er für jede neue Anzeigeart geändert werden (enge Kopplung). Korrektur: eine einzige Beziehung zum Interface, hier eine Aggregation mit 1 am Sessionplan und 0..* an Anzeige. Der Sessionplan kennt dann nur den Vertrag, nicht die Geräte (lose Kopplung); eine Assoziation ist mit dieser Begründung ebenfalls richtig.

  1. Das Technikteam setzt den korrigierten Entwurf in Java um. Entscheide für jede der markierten Anweisungen, ob der Compiler sie akzeptiert, und begründe kurz. (4 Punkte)
Sessionplan plan = new Sessionplan();
Anzeige a1 = new Tuerschild("C.204");                        // (1)
Tuerschild t1 = new Infomonitor("Foyer", "10.120.0.57");     // (2)
plan.anmelden(new Infomonitor("Mensa", "10.120.0.52"));      // (3)
Tuerschild t2 = a1;                                          // (4)

Je Anweisung 1 Punkt für die richtige Entscheidung mit Begründung:

  1. wird übersetzt: Eine Variable vom Typ eines Interfaces darf auf ein Objekt jeder Klasse verweisen, die das Interface realisiert.

  2. Fehler: Tuerschild und Infomonitor realisieren beide Anzeige, stehen aber in keiner Beziehung zueinander. Ein Infomonitor ist kein Türschild.

  3. wird übersetzt: anmelden() erwartet eine Anzeige, und ein Infomonitor ist eine Anzeige.

  4. Fehler: Der Compiler kennt von a1 nur den Typ Anzeige, und nicht jede Anzeige ist ein Türschild. Erst eine ausdrückliche Typumwandlung (Tuerschild) a1 würde übersetzt; mehr dazu im Artikel Polymorphie.

  1. Implementiere in Pseudocode die Methode alleAnzeigenAktualisieren(). Jede erreichbare Anzeige wird aktualisiert. Die nicht erreichbaren Anzeigen sammelt die Methode und gibt sie als Liste zurück, damit das Technikteam gezielt nachsehen kann. (5 Punkte)
privat alleAnzeigenAktualisieren(): Liste von Anzeige
    stoerungen := neue leere Liste von Anzeige
    für jede a vom Typ Anzeige in anzeigen
        wenn a.istErreichbar() dann
            a.aktualisieren(this)
        sonst
            stoerungen.hinzufuegen(a)
        Ende wenn
    Ende für
    Rückgabe stoerungen
Ende alleAnzeigenAktualisieren

Bewertung: leere Liste anlegen (1 Punkt), Schleife über alle Elemente von anzeigen (1 Punkt), Prüfung mit istErreichbar() (1 Punkt), Aufruf aktualisieren(this) mit dem Sessionplan als Argument (1 Punkt), nicht erreichbare Anzeigen sammeln und die Liste zurückgeben (1 Punkt).

  1. Svenja Krämer von der Schul-IT OWL fällt auf, dass Türschilder und Infomonitore beide eine IP-Adresse und eine VLAN-ID brauchen und istErreichbar() bei beiden gleich arbeitet (Ping an die IP-Adresse). Beide Gerätearten hängen im VLAN 120. Sie schlägt eine abstrakte Klasse Netzwerkgeraet vor, die das Interface Anzeige realisiert, die gemeinsamen Attribute für ihre Unterklassen zugänglich enthält und istErreichbar() implementiert. Wie dargestellt wird, legt weiterhin jede Geräteart selbst fest.

Außerdem soll der Sessionplan im Livestream aus der Aula eingeblendet werden. Die neue Klasse StreamEinblendung (privates Attribut szene als Text, öffentlicher Konstruktor mit diesem Wert) ist eine Anzeige, aber kein eigenes Netzwerkgerät: Sie gilt als erreichbar, solange der Stream läuft.

Zeichne den geänderten Ausschnitt des Klassendiagramms mit Anzeige, Netzwerkgeraet, Tuerschild, Infomonitor und StreamEinblendung. (6 Punkte)

Lösung: abstrakte Klasse Netzwerkgeraet und Klasse StreamEinblendung realisieren Anzeige, Tuerschild und Infomonitor erben von Netzwerkgeraet (mit KI erstellt)

Bewertung (6 Punkte):

  • Netzwerkgeraet als abstrakte Klasse gekennzeichnet (1 Punkt)

  • Realisierung von Netzwerkgeraet zu Anzeige; Tuerschild und Infomonitor haben keine eigene Realisierung mehr (1 Punkt)

  • Vererbung von Tuerschild und Infomonitor zu Netzwerkgeraet (1 Punkt)

  • #ipAdresse, #vlanId und istErreichbar() nur in Netzwerkgeraet; ipAdresse nicht mehr in Infomonitor (1 Punkt)

  • aktualisieren(plan: Sessionplan) in beiden Unterklassen, nicht in Netzwerkgeraet implementiert (1 Punkt)

  • StreamEinblendung realisiert Anzeige direkt und implementiert beide Methoden; sie erbt nicht von Netzwerkgeraet (1 Punkt)

Netzwerkgeraet muss abstrakt sein, weil es aktualisieren() aus dem Interface übernimmt, aber nicht implementiert. Wer +{abstract} aktualisieren(plan: Sessionplan) zusätzlich in Netzwerkgeraet notiert, macht das ausdrücklich sichtbar – das ist ebenfalls richtig. Der Konstruktor des Türschilds erhält jetzt auch die IP-Adresse; die VLAN-ID kann fest 120 sein oder als weiterer Parameter übergeben werden.

  1. Kurz vor dem Barcamp kommen drei weitere Wünsche auf. Entscheide jeweils, ob ein Interface, eine abstrakte Klasse oder eine konkrete Klasse die passende Lösung ist, und begründe Deine Entscheidung mit den Eigenschaften dieser drei Bausteine. Gib bei Wunsch 1 außerdem an, welche Beziehungen Infomonitor danach im Klassendiagramm hat. (7 Punkte)
  1. Die Infomonitore und das Bühnenlicht in der Aula (Klasse Buehnenlicht, gesteuert über einen DMX-Kanal, keine Anzeige) sollen sich über dieselbe Methode setHelligkeit(prozent: Integer) dimmen lassen. Wie die Helligkeit eingestellt wird, ist bei beiden völlig verschieden.

  2. Emre möchte in einem Test nur die Erreichbarkeit prüfen und schreibt Netzwerkgeraet testgeraet = new Netzwerkgeraet("10.120.0.60", 120);. Weil der Compiler das ablehnt, schlägt er vor, Netzwerkgeraet konkret zu machen.

  3. Für den Haupteingang gibt es ein großes Türschild mit Farbdisplay. Es kann alles, was ein normales Türschild kann, und hebt zusätzlich die gerade laufende Session farbig hervor.

Je Wunsch 1 Punkt für die passende Entscheidung und 1 Punkt für die Begründung, bei Wunsch 1 ein weiterer Punkt für die Beziehungen von Infomonitor (Vererbung zu Netzwerkgeraet und zusätzlich Realisierung von Dimmbar):

  1. Interface, z. B. Dimmbar: Gemeinsam ist nur der Vertrag „lässt sich dimmen“, es gibt keine gemeinsame Implementierung und keine gemeinsamen Attribute. Infomonitor erbt schon von Netzwerkgeraet; eine zweite Oberklasse ist in Java oder C# nicht möglich, ein weiteres Interface dagegen schon. Buehnenlicht ist weder Anzeige noch Netzwerkgerät und kann Dimmbar trotzdem realisieren.
Lösung zu Wunsch 1: Infomonitor erbt von Netzwerkgeraet und realisiert zusätzlich das Interface Dimmbar, das auch Buehnenlicht realisiert (mit KI erstellt)
  1. Netzwerkgeraet bleibt abstrakt: Von einer abstrakten Klasse lassen sich keine Objekte erzeugen, deshalb lehnt der Compiler die Anweisung ab. Würde Netzwerkgeraet konkret, müsste es auch aktualisieren() aus dem Interface implementieren, obwohl ein Netzwerkgerät „an sich“ nicht weiß, wie es etwas darstellen soll. Für den Test genügt ein Objekt einer konkreten Unterklasse wie Tuerschild; die geerbte, fertig implementierte Methode istErreichbar() lässt sich darüber prüfen.

  2. Konkrete Klasse als Unterklasse von Tuerschild, z. B. FarbTuerschild: Auch eine konkrete Klasse kann Oberklasse sein. Das neue Schild übernimmt alles vom Türschild, überschreibt aktualisieren() und ergänzt die farbige Hervorhebung. Tuerschild bleibt konkret, weil es weiterhin normale Türschilder als Objekte gibt; ein Interface brächte die vorhandene Implementierung nicht mit.

  1. Mia Schröder schlägt vor, das Interface Anzeige ganz zu streichen und Netzwerkgeraet zur gemeinsamen Oberklasse aller Anzeigen zu machen: „Eine Ebene weniger, gleiche Funktion.“ Nimm Stellung zu diesem Vorschlag. (3 Punkte)

Beispiel für eine Stellungnahme (3 Punkte): Der Vorschlag würde für Türschilder und Infomonitore funktionieren, passt aber nicht zur Stream-Einblendung. Sie müsste IP-Adresse, VLAN-ID und Ping erben, die sie gar nicht hat, oder der Sessionplan bräuchte doch wieder eine zweite Beziehung. Der Sessionplan wäre außerdem an eine Klasse mit Implementierung gebunden statt an einen schlanken Vertrag. Das Interface trennt den Vertrag („kann aktualisiert werden“) von der gemeinsamen Implementierung für netzwerkfähige Geräte. Ich würde es deshalb beibehalten.

Eine Gegenposition ist nur mit tragfähiger Begründung richtig, z. B.: Entfällt die Stream-Einblendung und sind sicher keine weiteren Anzeigearten geplant, reicht für ein eintägiges Barcamp auch die einfachere Struktur (Prinzip „YAGNI“).

Aufgabe 2.3: Erinnerungen in der Mitfahr-App (Kernkompetenz)

(ca. 22 min Bearbeitungszeit)

Du bist Auszubildende*r im FIA-Projektteam der Mitfahr-App „RatzFahrt“ des Ratz und Rübe Berufskollegs in Bielefeld. Mitfahrende sollen vor jeder gebuchten Fahrt erinnert werden. Oksana Melnyk hat mit der Datenschutzbeauftragten Andrea Wolf festgelegt, dass nur Kanäle genutzt werden, für die keine zusätzlichen personenbezogenen Daten erhoben werden müssen: E-Mail an das Schulkonto und Push-Benachrichtigung der Web-App. Lukas Hofmann, der einen Screenreader nutzt, bevorzugt Push-Benachrichtigungen, weil sein Screenreader sie sofort vorliest.

Auszug aus dem Pflichtenheft:

  • Die Klasse Erinnerungsdienst versendet Erinnerungen. Sie hat einen öffentlichen Konstruktor ohne Parameter, ein nur klassenintern sichtbares Attribut vorlaufMinuten (ganze Zahl, Standardwert 30) und eine nur klassenintern sichtbare Referenz kanal vom Typ des Interfaces Benachrichtigungskanal.

  • Mit der öffentlichen Methode setKanal wird der Kanal gesetzt oder ausgetauscht.

  • Die öffentliche Methode erinnern erhält ein Objekt der vorhandenen Klasse Buchung und gibt zurück, ob die Erinnerung versendet wurde.

  • Das Interface Benachrichtigungskanal legt die öffentlichen Methoden senden (Übergabe: ein Objekt der vorhandenen Klasse Nutzer als Empfänger und ein Text; Rückgabe: Erfolg ja/nein) und getName (Rückgabe: Name des Kanals als Text) fest.

  • Die Klassen EMailKanal (privates Attribut absender, Text) und PushKanal (privates Attribut dienstUrl, Text) realisieren das Interface. Beide haben einen öffentlichen Konstruktor, dem der Wert ihres Attributs übergeben wird.

  • Vorhandene Methoden: Buchung.getMitfahrer(): Nutzer, Buchung.getAbfahrt(): String (z. B. „Mo 07:00“) und Buchung.getHaltepunkt(): String (z. B. „Gütersloh Hbf“).

  1. Entwirf nach dem Pflichtenheft ein UML-Klassendiagramm mit den Klassen Erinnerungsdienst, EMailKanal, PushKanal und dem Interface Benachrichtigungskanal einschließlich aller Beziehungen. Die Klassen Buchung und Nutzer musst Du nicht zeichnen. (8 Punkte)
Lösung: Erinnerungsdienst mit Referenz auf das Interface Benachrichtigungskanal, das EMailKanal und PushKanal realisieren (mit KI erstellt)

Bewertung (8 Punkte):

  • Interface mit <<interface>> und beiden Methodensignaturen (2 Punkte)

  • Erinnerungsdienst mit Attributen (inkl. Standardwert), Konstruktor und beiden Methoden mit vollständiger Signatur (2 Punkte)

  • EMailKanal und PushKanal mit Attribut, Konstruktor und den beiden implementierten Methoden (2 Punkte)

  • zwei Realisierungen (gestrichelt, hohles Dreieck am Interface) (1 Punkt)

  • Beziehung Erinnerungsdienst – Benachrichtigungskanal (Aggregation, Assoziation oder Komposition) (1 Punkt)

Hinweis: Ob die Referenz kanal zusätzlich als Attribut eingetragen wird, ist Geschmackssache – hier verlangt sie das Pflichtenheft. Multiplizitäten sind nicht gefordert; 0..1 drückt aus, dass anfangs noch kein Kanal gesetzt sein kann.

  1. Implementiere in Pseudocode die Methode erinnern() der Klasse Erinnerungsdienst. Der Text soll Abfahrtszeit und Haltepunkt enthalten, z. B. „RatzFahrt: Deine Fahrt startet Mo 07:00 ab Gütersloh Hbf.“ Ist noch kein Kanal gesetzt, wird nichts versendet. (4 Punkte)
öffentlich erinnern(b: Buchung): Boolean
    wenn kanal = null dann
        Rückgabe falsch
    Ende wenn
    text := "RatzFahrt: Deine Fahrt startet " + b.getAbfahrt() + " ab " + b.getHaltepunkt() + "."
    Rückgabe kanal.senden(b.getMitfahrer(), text)
Ende erinnern

Bewertung: Prüfung auf fehlenden Kanal mit Rückgabe falsch (1 Punkt), Text aus den Buchungsdaten zusammensetzen (1 Punkt), Aufruf von senden() auf dem Interface-Objekt kanal mit dem Mitfahrer als Empfänger (1 Punkt), Rückgabe des Ergebnisses von senden() (1 Punkt).

Der Erinnerungsdienst weiß nicht, ob gerade eine E-Mail oder eine Push-Nachricht verschickt wird – er ruft nur die Methode des Vertrags auf.

  1. Stelle in Pseudocode die Anweisungen dar, mit denen
  • ein Erinnerungsdienst erzeugt wird, der zunächst E-Mails mit dem Absender ratzfahrt@rrb.example versendet, und

  • für die Buchung buchungLukas die Erinnerung als Push-Nachricht über den Dienst https://push.ratzfahrt.example/v1 versendet wird.

(3 Punkte)

dienst := new Erinnerungsdienst()
dienst.setKanal(new EMailKanal("ratzfahrt@rrb.example"))

dienst.setKanal(new PushKanal("https://push.ratzfahrt.example/v1"))
dienst.erinnern(buchungLukas)

Bewertung: Erzeugen des Dienstes und Setzen des E-Mail-Kanals (1 Punkt), Austausch des Kanals über setKanal() mit einem neuen PushKanal-Objekt (1 Punkt), Aufruf von erinnern() mit der Buchung (1 Punkt).

Eleganter wäre es, den bevorzugten Kanal je Nutzer*in zu speichern; das ist hier aber nicht gefragt.

  1. Jonas Wolff hat für einen Test einen weiteren Kanal programmiert. Beim Übersetzen meldet der Java-Compiler Fehler:
public class TestKanal extends Benachrichtigungskanal {
    private String protokoll;

    public TestKanal(String protokoll) {
        this.protokoll = protokoll;
    }

    public boolean senden(Nutzer empfaenger, String text) {
        protokoll = protokoll + "\n" + empfaenger.getPseudonym() + ": " + text;
        return true;
    }
}

// in einer Testmethode:
Benachrichtigungskanal standard = new Benachrichtigungskanal();
dienst.setKanal(standard);

Analysiere den Quelltext. Erläutere drei Fehler und gib jeweils an, wie sie behoben werden. (6 Punkte)

Je Fehler 2 Punkte (1 Punkt Erläuterung, 1 Punkt Korrektur):

  1. extends statt implements: Benachrichtigungskanal ist ein Interface. Von einem Interface wird nicht geerbt, es wird realisiert. Korrektur: public class TestKanal implements Benachrichtigungskanal.

  2. getName() fehlt: Eine Klasse, die ein Interface realisiert, muss alle seine abstrakten Methoden implementieren, sonst ist sie selbst abstrakt (und müsste so gekennzeichnet werden). Korrektur: public String getName() { return "Test"; } ergänzen.

  3. Interface instanziiert: new Benachrichtigungskanal() ist nicht möglich, weil von Interfaces (wie von abstrakten Klassen) keine Objekte erzeugt werden können. Korrektur: ein Objekt einer realisierenden Klasse erzeugen, z. B. Benachrichtigungskanal standard = new TestKanal(""); – der Variablentyp darf das Interface bleiben.

Ebenfalls als Hinweis richtig: Das Attribut protokoll sollte für den Test über einen Getter auslesbar sein, sonst kann der Test das Ergebnis nicht prüfen.

  1. Begründe, warum für die Kanäle ein Interface und keine gemeinsame (abstrakte) Oberklasse gewählt wurde. (3 Punkte)

Mögliche Begründungen (3 Punkte, je nach Tiefe):

  • Die Kanäle haben keine gemeinsame Implementierung und keine gemeinsamen Attribute – E-Mail und Push werden völlig unterschiedlich versendet. Es gibt also nichts zu vererben, nur einen gemeinsamen Vertrag.

  • Der Erinnerungsdienst hängt nur vom Vertrag ab (lose Kopplung). Neue Kanäle können ergänzt und zur Laufzeit über setKanal() getauscht werden, ohne den Erinnerungsdienst zu ändern.

  • Eine Klasse kann beliebig viele Interfaces realisieren, aber nur von einer Klasse erben. Ein PushKanal könnte so z. B. zusätzlich von einer vorhandenen Klasse für HTTP-Verbindungen erben.

  • Für Tests lässt sich leicht eine Attrappe (wie TestKanal) einsetzen.


Hinweis zur Nachnutzung als Open Educational Resource (OER)

Dieser Artikel und seine Texte, Bilder, Grafiken, Code und sonstiger Inhalt sind - sofern nicht anders angegeben - lizenziert unter CC BY 4.0. Nennung gemäß TULLU-Regel bitte wie folgt: “Übungsaufgaben zu Vererbung, abstrakten Klassen und Interfaces” von oer-informatik.de (H. Stein), Lizenz: CC BY 4.0. Der Artikel wurde unter https://oer-informatik.de/uml-klassendiagramm-vererbung-uebungsaufgaben veröffentlicht, die Quelltexte sind in weiterverarbeitbarer Form verfügbar im Repository unter https://gitlab.com/oer-informatik/uml/uml-diagramme. Erstellt mit KI-Assistenz; von mir umfassend redaktionell überarbeitet, geprüft und freigegeben. Stand: 29.10.2026.

[Kommentare zum Artikel lesen, schreiben] / [Artikel teilen] / [gitlab-Issue zum Artikel schreiben]

Kommentare gerne per Mastodon, Verbesserungsvorschläge per gitlab issue (siehe oben). Beitrag teilen: