java\08_OOP_Entwurfsmuster\.Objektorientierung kommt in der AP2 selten als „reine Theorie“ vor, sondern fast immer als Kette in einer größeren Aufgabe: Zuerst wird ein Klassendiagramm gezeichnet oder ergänzt (oft mit abstrakter Klasse oder Interface), dann soll ein Begriff wie Polymorphie erläutert werden, und zum Schluss werden einzelne Methoden in Pseudocode implementiert. Entwurfsmuster tauchen in derselben Form auf: Muster erkennen, im Klassendiagramm umsetzen, Ablauf zeigen, Methode programmieren. Beispiele: S22 Teil 1 Aufgabe 3 (Observer, 30 P), S23 Teil 1 Aufgabe 2b (Polymorphie und Fabrikmethode, 22 P), S26 Teil 2 Aufgabe 2 (abstrakte Klasse und dynamische Bindung, 20 P), W24 Teil 1 Aufgabe 4b (Entwurfsmuster, 10 P).
| Unterthema | Kategorie | Typische Punkte | Wichtigste Fähigkeit |
|---|---|---|---|
| OOP-01 Grundkonzepte | Oft | 2–7 (Teil einer größeren Aufgabe) | Klasse aus Text oder Diagramm umsetzen: Sichtbarkeiten, Konstruktor, static, Getter/Setter |
| OOP-02 Vererbung, abstrakte Klassen, Interfaces | Oft | 4–12 | Hierarchie modellieren, Fallunterscheidungen nach einem Typ-Attribut durch Vererbung ersetzen |
| OOP-03 Polymorphie, Überladen/Überschreiben, Generics | Oft | 3–10 | Dynamische Bindung erklären, Aufrufe richtig auswerten, generische Klasse erläutern |
| OOP-04 Erzeugungsmuster und Musterkategorien | Wahrscheinlich | 2–14 | Vorteile von Mustern, Kategorien mit Beispielen, Singleton, Fabrikmethode |
| OOP-05 Verhaltens- und Strukturmuster | Wahrscheinlich | 4–30 | Observer vollständig (Klassendiagramm, Ablauf, Code), Muster zu einem Szenario wählen und begründen |
Kategorien laut Themenanalyse: Der ganze Block kommt in 6 von 9 Prüfungen vor („Meistens“). Die Unterthemen OOP-01 bis OOP-03 gehören zu „OOP-Konzepte“ (5/9, Oft), OOP-04 und OOP-05 zu „Entwurfsmuster“ (3/9, Wahrscheinlich).
Vorkommen: Teil von „OOP-Konzepte“ (5/9: S22, W22, S23, S24, S26) · ausdrücklich gefragt in S22 T1 A3a (Sichtbarkeiten im Klassendiagramm), S24 T1 A3bc (Objekt über überladenen Konstruktor erzeugen), S26 T2 A2a (Klassen mit vollständigen Signaturen und Sichtbarkeiten) · vorausgesetzt in jeder Algorithmusaufgabe mit Klassengerüst
Eine Klasse ist der Bauplan (Datentyp) für gleichartige Objekte. Sie legt fest, welche Attribute (Daten, Eigenschaften) und welche Methoden (Operationen, Verhalten) jedes Objekt hat. Ein Objekt (Instanz, Exemplar) ist ein konkretes Exemplar einer Klasse, das zur Laufzeit mit new erzeugt wird. Jedes Objekt hat eigene Attributwerte (seinen Zustand) und eine eigene Identität – zwei Objekte mit gleichen Werten sind trotzdem zwei verschiedene Objekte.
Konto, zwei Objekte mit eigenem Zustand (Objekte werden im UML-Objektdiagramm unterstrichen benannt)| Begriff | Bedeutung | Beispiel (Konto) |
|---|---|---|
| Attribut | Eigenschaft, die jedes Objekt der Klasse besitzt | saldo : double |
| Methode | Operation, die ein Objekt ausführen kann; Aufruf = „Nachricht an das Objekt“ | abheben(betrag : double) : boolean |
| Zustand | aktuelle Werte aller Attribute eines Objekts | k1: Saldo 250,00 €, Dispo 500,00 € |
| Identität | jedes Objekt ist eindeutig, unabhängig von seinen Werten | k1 und k2 sind verschiedene Objekte |
| Instanziierung | Erzeugen eines Objekts mit new; dabei läuft der Konstruktor | k1 := new Konto("DE01", "Yilmaz") |
Datenkapselung (Geheimnisprinzip, information hiding) bedeutet: Die Attribute eines Objekts sind nach außen verborgen (private). Andere Objekte greifen nur über die öffentlichen Methoden zu. Dadurch entscheidet allein die Klasse, welche Änderungen erlaubt sind – ein Konto lässt sich nur über abheben verringern, und abheben prüft das Limit.
| UML | Java | Bedeutung | eigene Klasse | gleiches Paket | Unterklasse | überall |
|---|---|---|---|---|---|---|
+ | public | öffentlich | ✓ | ✓ | ✓ | ✓ |
# | protected | geschützt | ✓ | ✓ (nur Java) | ✓ | – |
~ | (kein Schlüsselwort) | paketweit | ✓ | ✓ | nur im selben Paket | – |
- | private | privat | ✓ | – | – | – |
In Prüfungsantworten genügt: protected = Zugriff in der Klasse selbst und in abgeleiteten Klassen (Unterklassen). Dass Java zusätzlich den Zugriff im selben Paket erlaubt, ist eine Java-Besonderheit.
- (private), Methoden + (public). Reine Hilfsmethoden, die nur intern gebraucht werden, sind -. Attribute, auf die Unterklassen direkt zugreifen sollen, sind #.getSaldo() : double, bei boolean üblich isGesperrt() : boolean.setDispo(dispo : double) : void. Ein guter Setter prüft den Wert und lehnt ungültige Werte ab.final gekennzeichnet.k.getSaldo()), nie das Attribut direkt (k.saldo).thisnew automatisch aufgerufen und bringt das neue Objekt in einen gültigen Anfangszustand (Attribute setzen, Startwerte, Zähler erhöhen).this verweist auf das aktuelle Objekt:
this.saldo := saldo – unterscheidet das Attribut vom gleichnamigen Parameter,this(kontoNr, inhaber, 0.0) – ruft einen anderen Konstruktor derselben Klasse auf (muss die erste Anweisung sein) und vermeidet doppelten Code,bank.registrieren(this) – übergibt das eigene Objekt an ein anderes Objekt (wichtig beim Observer-Muster).Ein mit static gekennzeichnetes Attribut gehört zur Klasse, nicht zu einem einzelnen Objekt. Es existiert genau einmal und wird von allen Objekten geteilt. Eine statische Methode wird über den Klassennamen aufgerufen (Konto.getAnzahlKonten()), auch wenn noch kein Objekt existiert. Sie hat kein this und kann deshalb nicht direkt auf Instanzattribute zugreifen.
static final), Hilfsmethoden (Math.sqrt), Fabrikmethoden und Singleton (OOP-04).{static} hinter der Signatur.static deklariert (z. B. saldo), teilen sich alle Konten einen einzigen Saldo. Umgekehrt zählt ein nicht-statischer Zähler nur im eigenen Objekt und steht bei jedem neuen Objekt wieder auf dem Startwert.Konto mit statischem Zähler (unterstrichen), unveränderlicher Kontonummer und zwei KonstruktorenPseudocode
klasse Konto
- anzahlKonten : integer := 0 {static}
- kontoNr : string
- inhaber : string
- saldo : double
- dispo : double
konstruktor Konto(kontoNr : string,
inhaber : string, dispo : double)
this.kontoNr := kontoNr
this.inhaber := inhaber
saldo := 0.0
this.dispo := 0.0
setDispo(dispo)
anzahlKonten := anzahlKonten + 1
Ende konstruktor
konstruktor Konto(kontoNr : string, inhaber : string)
this(kontoNr, inhaber, 0.0)
Ende konstruktor
methode setDispo(dispo : double) : void
wenn dispo >= 0 und dispo <= 2000 dann
this.dispo := dispo
Ende wenn
Ende methode
methode abheben(betrag : double) : boolean
wenn betrag > 0 und saldo - betrag >= -dispo dann
saldo := saldo - betrag
rückgabe true
Ende wenn
rückgabe false
Ende methode
methode getAnzahlKonten() : integer {static}
rückgabe anzahlKonten
Ende methode
Ende klasse
Java (mit equals, siehe unten „Identität und Gleichheit“)
class Konto {
private static int anzahlKonten = 0;
private final String kontoNr;
private String inhaber;
private double saldo;
private double dispo;
public Konto(String kontoNr, String inhaber, double dispo) {
this.kontoNr = kontoNr;
this.inhaber = inhaber;
this.saldo = 0.0;
this.dispo = 0.0;
setDispo(dispo);
anzahlKonten = anzahlKonten + 1;
}
public Konto(String kontoNr, String inhaber) {
this(kontoNr, inhaber, 0.0);
}
public void setDispo(double dispo) {
if (dispo >= 0 && dispo <= 2000) {
this.dispo = dispo;
}
}
public boolean abheben(double betrag) {
if (betrag > 0 && saldo - betrag >= -dispo) {
saldo = saldo - betrag;
return true;
}
return false;
}
public static int getAnzahlKonten() {
return anzahlKonten;
}
public double getSaldo() {
return saldo;
}
@Override
public boolean equals(Object o) {
return o instanceof Konto && kontoNr.equals(((Konto) o).kontoNr);
}
}
Aufruf: Konto k1 = new Konto("DE01", "Yilmaz", 500.0); und Konto k2 = new Konto("DE02", "Novak"); – danach liefert Konto.getAnzahlKonten() den Wert 2. k2.abheben(50.0) liefert false, weil k2 keinen Dispo hat.
Der IHK-Belegsatz legt nur die Kontrollstrukturen fest. Für Klassen ist jede eindeutige Schreibweise zulässig. Diese wird in den Lösungen verwendet:
| Element | Pseudocode | Java |
|---|---|---|
| Klasse | klasse Konto … Ende klasse | public class Konto { … } |
| Attribut / statisches Attribut | - saldo : double / - anzahl : integer {static} | private double saldo; / private static int anzahl; |
| Konstruktor | konstruktor Konto(nr : string) … Ende konstruktor | public Konto(String nr) { … } |
| Methode | methode abheben(betrag : double) : boolean … Ende methode | public boolean abheben(double betrag) { … } |
| Rückgabe | rückgabe saldo (auch return) | return saldo; |
| eigenes Objekt / anderer Konstruktor | this.saldo / this(nr, 0.0) | this.saldo / this(nr, 0.0); |
| Objekt erzeugen | k : Konto := new Konto("DE01") | Konto k = new Konto("DE01"); |
| Methode aufrufen | k.abheben(20.0), statisch: Konto.getAnzahlKonten() | genauso |
| Vererbung / Interface | klasse Girokonto erbt von Konto / klasse X implementiert Druckbar | extends / implements |
| abstrakt | abstrakte klasse, abstrakte methode zinsen() : double | abstract |
| Oberklasse | super(nr), super.zinsen() | super(nr);, super.zinsen() |
| kein Objekt | null, Prüfung wenn k <> null dann | null, if (k != null) |
methode oder funktion, rückgabe oder return schreibst, ist egal – Hauptsache eindeutig und durchgehend gleich. Wenn die Aufgabe die Methode nur „in Pseudocode implementieren“ lässt, genügt die Methode selbst; die umgebende Klasse musst du nicht hinschreiben.null, Identität und GleichheitEine Variable vom Typ einer Klasse enthält nicht das Objekt selbst, sondern einen Verweis (Referenz) darauf. Eine Zuweisung k3 := k1 kopiert nur den Verweis: Danach zeigen beide Variablen auf dasselbe Objekt (Aliasing), und eine Änderung über k3 ist auch über k1 sichtbar. null bedeutet „zeigt auf kein Objekt“; ein Methodenaufruf auf null führt zur NullPointerException.
k1 und k3 verweisen auf dasselbe Objekt, k4 auf keins| Vergleich | prüft | Beispiel (Abb. A-3, equals vergleicht die Kontonummer) |
|---|---|---|
== bei Objekten | Identität: Zeigen beide Referenzen auf dasselbe Objekt? | k1 == k3 → true · k1 == k2 → false |
equals | Gleichheit: Sind die Objekte inhaltlich gleich? Nur wenn die Klasse equals überschreibt – sonst erbt sie Object.equals, das wie == arbeitet. | Ein neues Objekt k5 mit der Kontonummer „DE01“: k1.equals(k5) → true, k1 == k5 → false |
== bei int, double, boolean | Wertvergleich (primitive Typen haben keine Identität) | 3 == 3 → true |
integer, double, string, boolean, eine Klasse oder List<…>). Sichtbarkeit -.static (unterstrichen).this(…) den vollständigen Konstruktor aufrufen.wenn … dann), Zustand ändern, Erfolg melden (boolean) oder ein Objekt bzw. null zurückgeben.void Konto(…)) – dann ist es eine normale Methode und kein Konstruktor.saldo := saldo statt this.saldo := saldo: Der Parameter wird sich selbst zugewiesen, das Attribut bleibt unverändert.+ (public) oder Setter ohne Prüfung – die Kapselung ist damit wirkungslos.this) → Compilerfehler.== statt equals verglichen, insbesondere Strings in Java. Im Pseudocode ist = erlaubt, wenn die Aufgabe es zulässt (S26: Hinweis, dass Strings direkt verglichen werden dürfen).null sein kann, ohne vorher zu prüfen.Vorkommen: Teil von „OOP-Konzepte“ (5/9) · S23 T1 A2bb (9 P: abstrakte Klasse mit Unterklassen statt Fallunterscheidungen), S23 T1 A2bd (4 P: überschreibende Methoden), S26 T2 A2a (12 P: abstrakte Klasse, Vererbung, Aggregation), S22 T1 A3bb/bc (6 P: Interface-Methoden ergänzen, Realisierung erläutern)
Vererbung ist eine „ist-ein“-Beziehung: Die Unterklasse (abgeleitete Klasse, Subklasse) übernimmt alle Attribute und Methoden der Oberklasse (Basisklasse, Superklasse), kann neue hinzufügen und geerbte Methoden überschreiben. Gemeinsamkeiten stehen nur einmal in der Oberklasse (Generalisierung), Besonderheiten in den Unterklassen (Spezialisierung).
class Azubi extends Mitarbeiter – genau eine Oberklasse. UML: durchgezogene Linie mit hohlem Dreieck an der Oberklasse.# (protected) machen.superKonstruktoren werden nicht vererbt. Jeder Konstruktor einer Unterklasse ruft als erste Anweisung einen Konstruktor der Oberklasse auf: ausdrücklich mit super(…) oder – wenn man nichts schreibt – automatisch mit super(). Das automatische super() funktioniert nur, wenn die Oberklasse einen parameterlosen Konstruktor hat. Der Oberklassenteil eines Objekts wird also immer zuerst initialisiert („von oben nach unten“).
class Person {
private String name;
public Person(String name) {
this.name = name;
System.out.println("Person " + name + " angelegt");
}
}
class Kunde extends Person {
private int kundenNr;
public Kunde(String name, int kundenNr) {
super(name); // muss die erste Anweisung sein
this.kundenNr = kundenNr;
System.out.println("Kunde " + kundenNr + " angelegt");
}
}
class Firmenkunde extends Kunde {
private String firma;
public Firmenkunde(String name, int kundenNr, String firma) {
super(name, kundenNr);
this.firma = firma;
System.out.println("Firmenkunde " + firma + " angelegt");
}
}
Aufruf und Ausgabe
new Firmenkunde("Kaya", 4711, "Kaya Bau GmbH");
Person Kaya angelegt
Kunde 4711 angelegt
Firmenkunde Kaya Bau GmbH angelegt
Mit super.methode() ruft eine Unterklasse außerdem die Oberklassen-Version einer überschriebenen Methode auf, z. B. rückgabe super.urlaubstage() + 2.
protected und finalprotected (#): sichtbar in der eigenen Klasse und in allen Unterklassen – für Attribute, die Unterklassen direkt lesen oder setzen sollen.final bei einer Klasse: keine Unterklassen möglich (Beispiel String). Bei einer Methode: darf nicht überschrieben werden. Bei einem Attribut: darf nur einmal zugewiesen werden; static final = Konstante.abstract; UML: Klassenname kursiv und/oder {abstract}, abstrakte Methoden ebenfalls kursiv bzw. mit {abstract}.new Mitarbeiter(…) ist verboten). Variablen, Parameter und Listen dürfen aber diesen Typ haben (List<Mitarbeiter>).public abstract) und Konstanten, keine Instanzattribute und keinen Konstruktor. (Seit Java 8 sind default-Methoden möglich – für die Prüfung nicht nötig.)class Rechnung implements Druckbar, Exportierbar.«interface» über dem Namen; Realisierung (Implementierung) = gestrichelte Linie mit hohlem Dreieck am Interface.Rechnung erbt von Dokument und realisiert zwei Interfaces; Mahnung realisiert nur Druckbar| Abstrakte Klasse | Interface | |
|---|---|---|
| Beziehung | „ist ein“ – gemeinsamer Oberbegriff einer Familie verwandter Klassen | „kann“ – Fähigkeit/Rolle, auch für nicht verwandte Klassen |
| Objekte erzeugbar? | nein | nein |
| als Typ verwendbar? | ja (Variable, Parameter, Liste) | ja (Variable, Parameter, Liste) |
| Attribute | ja, auch Instanzattribute mit Werten je Objekt | nur Konstanten |
| Konstruktor | ja (wird von Unterklassen mit super aufgerufen) | nein |
| Methoden | abstrakte und fertig implementierte | nur Signaturen (abstrakt) |
| Anzahl je Klasse | höchstens eine Oberklasse (extends) | beliebig viele (implements) |
| UML | Name kursiv / {abstract}; Vererbung: Linie mit hohlem Dreieck | «interface»; Realisierung: gestrichelt mit hohlem Dreieck |
In Java und C# kann eine Klasse nur von einer Klasse erben. Grund ist das Diamond-Problem: Erben B und C von A und überschreiben beide die Methode m(), wäre bei einer Klasse D extends B, C unklar, welches m() gilt und ob die Attribute von A einmal oder doppelt vorhanden sind. Interfaces haben dieses Problem nicht, weil sie weder Zustand noch (verpflichtende) Implementierung mitbringen: Die implementierende Klasse schreibt die Methode selbst, es gibt also nur eine Implementierung. Deshalb gilt: eine Oberklasse, beliebig viele Interfaces. (C++ erlaubt Mehrfachvererbung und muss den Konflikt mit zusätzlichen Regeln lösen.)
m() erbt D?Ein klassischer Prüfungsfall (S23): Eine Klasse hat ein Attribut typ und mehrere Methoden, die alle mit derselben Fallunterscheidung (wenn typ = … dann …) arbeiten. Jeder neue Typ zwingt dazu, alle diese Methoden zu ändern – vergisst man eine, entsteht ein Fehler. Beispiel aus einer Personalverwaltung:
| Methode | typ = "ANGESTELLT" | typ = "AUSHILFE" | typ = "AZUBI" |
|---|---|---|---|
gehalt() | monatsgehalt | stundenlohn * stunden | 1000 + 100 * lehrjahr |
urlaubstage() | 30 | 0 | 28 |
Jede Unterklasse implementiert nur „ihre Spalte“ der Tabelle. Beispiel Aushilfe:
klasse Aushilfe erbt von Mitarbeiter
- stundenlohn : double
- stunden : double
konstruktor Aushilfe(name : string,
stundenlohn : double)
super(name)
this.stundenlohn := stundenlohn
stunden := 0.0
Ende konstruktor
methode setStunden(stunden : double) : void
this.stunden := stunden
Ende methode
methode gehalt() : double
rückgabe stundenlohn * stunden
Ende methode
methode urlaubstage() : integer
rückgabe 0
Ende methode
Ende klasse
In Java entspricht erbt von dem Schlüsselwort extends, die überschreibenden Methoden werden mit @Override markiert.
Azubi, "ANGESTELLT" → Angestellter).typ entfällt (auch aus dem Konstruktor).{abstract}) und in jeder Unterklasse mit genau ihrem Zweig implementiert.lehrjahr, stundenlohn) wandern in die passende Unterklasse.Vorteil des neuen Entwurfs: Ein neuer Typ ist eine neue Unterklasse; bestehende, getestete Klassen bleiben unverändert (Open-Closed-Prinzip: offen für Erweiterung, geschlossen für Änderung). Keine vergessenen Zweige, kürzere und verständlichere Methoden.
typ im neuen Entwurf stehen lassen.super(…) vergessen, obwohl die Oberklasse keinen parameterlosen Konstruktor hat.class D extends B, C – in Java nicht möglich.Vorkommen: Teil von „OOP-Konzepte“ (5/9) · Polymorphie: S23 T1 A2ba (4 P: Begriff erläutern), S26 T2 A2bb (4 P: Mechanismus nennen und erklären) · überschreibende Methoden: S23 T1 A2bd · Generics: W22 T2 A1c (3 P: generische Klasse erläutern), W22 T2 A1d (7 P: Sortieren einer List<T>) · Vergleichsfunktion als Parameter: W22 T2 A1b, S24 T2 A2a
Beispiel Stadtbibliothek: Die abstrakte Klasse Medium hat die abstrakte Methode leihfrist() : integer. Buch liefert 28 Tage, DVD 7 Tage, Zeitschrift 14 Tage. Eine Methode, die alle Medien einer Ausleihe durchläuft, ruft einfach m.leihfrist() auf – jedes Objekt antwortet mit seiner eigenen Implementierung:
methode laengsteFrist(medien : List<Medium>)
: integer
max : integer := 0
zähle i von 0 bis medien.size() - 1
m : Medium := medien.get(i)
frist : integer := m.leihfrist()
// welches leihfrist()? Das Objekt
// entscheidet (dynamische Bindung)
wenn frist > max dann
max := frist
Ende wenn
Ende zähle
rückgabe max
Ende methode
Medium, drei verschiedene Objekte, drei verschiedene ImplementierungenKommt später eine neue Medienart hinzu (z. B. Hoerbuch mit 21 Tagen), wird nur eine neue Unterklasse geschrieben – die Methode laengsteFrist bleibt unverändert.
| Form | Mechanismus | Entscheidung |
|---|---|---|
| dynamische Polymorphie (meist gemeint) | Überschreiben von Methoden in Unterklassen | zur Laufzeit (dynamische/späte Bindung) |
| statische Polymorphie | Überladen (gleicher Name, andere Parameterliste) | beim Übersetzen (statische/frühe Bindung) |
| parametrische Polymorphie | Generics (List<T>) | beim Übersetzen |
Bei Medium m := new DVD("Metropolis") ist Medium der statische Typ (deklarierter Typ der Variablen) und DVD der dynamische Typ (Klasse des Objekts, auf das m gerade zeigt).
m sind nur Methoden aufrufbar, die Medium kennt. Eine Methode, die nur DVD hat (z. B. laufzeitMinuten()), ist über m nicht aufrufbar.m.leihfrist() führt DVD.leihfrist() aus.this.leihfrist()), gilt ebenfalls der dynamische Typ des Objekts.| Überschreiben (Overriding) | Überladen (Overloading) | |
|---|---|---|
| Wo? | Oberklasse und Unterklasse (bzw. Interface und implementierende Klasse) | meist in derselben Klasse; auch eine Unterklasse kann eine geerbte Methode überladen |
| Signatur | gleicher Name, gleiche Parameterliste, kompatibler Rückgabetyp | gleicher Name, andere Parameterliste (Anzahl, Typen, Reihenfolge) |
| Wirkung | ersetzt die geerbte Implementierung (Zugriff auf das Original mit super.methode()) | zusätzliche Variante; die anderen bleiben bestehen |
| Bindung | dynamisch zur Laufzeit (dynamischer Typ des Objekts) | statisch beim Übersetzen (statische Typen der Argumente) |
| Java | Annotation @Override (Compiler prüft, ob wirklich überschrieben wird) | keine Annotation |
| Beispiel | DVD.leihfrist() ersetzt Medium.leihfrist() | suche(titel : string) und suche(isbn : string, auflage : integer) |
Eine Klasse Etikettendrucker hat zwei überladene Methoden drucke(m : Medium) (druckt „Medium“) und drucke(b : Buch) (druckt „Buch“).
Medium m = new Buch("Faust");
Buch b = new Buch("Woyzeck");
drucker.drucke(m); // Ausgabe: Medium (statischer Typ von m ist Medium!)
drucker.drucke(b); // Ausgabe: Buch
Obwohl m auf ein Buch zeigt, wird drucke(Medium) gewählt, denn welche überladene Variante passt, entscheidet der Compiler mit den statischen Typen. Dynamisch gebunden wird nur beim Überschreiben.
leihfrist(verlaengert : boolean)), ist das kein Überschreiben. Die geerbte leihfrist() bleibt unverändert und wird weiterhin aufgerufen. @Override würde diesen Fehler beim Übersetzen melden.instanceofMedium m := new DVD(…).DVD d = (DVD) m;. Der Compiler erlaubt ihn, wenn die Typen verwandt sind. Zeigt m zur Laufzeit aber nicht auf eine DVD, entsteht eine ClassCastException.instanceof prüfen: if (m instanceof DVD) { DVD d = (DVD) m; … } (ab Java 16 kürzer: if (m instanceof DVD d) { … }). In Pseudocode: wenn m ist DVD dann.instanceof-Abfragen sind ein Zeichen für schlechten Entwurf – meist gehört das Verhalten als überschriebene Methode in die Unterklassen.Eine generische Klasse hat einen oder mehrere Typparameter (Platzhalter für einen Datentyp), z. B. class Warteschlange<T>. Erst beim Verwenden wird der konkrete Typ festgelegt: Warteschlange<Patient>, Warteschlange<String>. Die bekanntesten generischen Klassen sind die Collections: List<T>, Map<K, V>.
Pseudocode
klasse Warteschlange<T>
- elemente : List<T>
konstruktor Warteschlange()
elemente := new List<T>()
Ende konstruktor
methode einreihen(e : T) : void
elemente.add(e)
Ende methode
methode naechster() : T
wenn elemente.size() = 0 dann
rückgabe null
Ende wenn
erster : T := elemente.get(0)
elemente.remove(erster)
rückgabe erster
Ende methode
Ende klasse
Verwendung (Java)
Warteschlange<Patient> praxis = new Warteschlange<>();
praxis.einreihen(new Patient("Ahmadi"));
praxis.einreihen(new Patient("Brandt"));
Patient naechster = praxis.naechster(); // kein Cast nötig
// praxis.einreihen("Brandt"); // Compilerfehler: String ist kein Patient
Vorteile gegenüber einer Klasse, die Elemente vom Typ Object speichert:
Warteschlange<Patient> lässt der Compiler nur Patient-Objekte hinein. Mit Object passt alles hinein, ein falscher Typ fällt erst zur Laufzeit als ClassCastException auf.Patient.PatientenWarteschlange, AuftragsWarteschlange …).Weitere Formen: generische Methode mit eigenem Typparameter (<T> T letztes(List<T> liste)), beschränkter Typparameter <T extends Medium> – dann dürfen auf T die Methoden von Medium aufgerufen werden. Typparameter stehen nur für Referenztypen: List<Integer>, nicht List<int>.
Ein generischer Algorithmus (Sortieren, Minimum, „ist die Liste sortiert?“) weiß nicht, wie zwei Objekte vom Typ T zu vergleichen sind. Deshalb bekommt er ein Objekt mit einer Vergleichsmethode übergeben – in Java das Interface Comparator<T> mit compare(a, b):
a kommt vor b (a ist „kleiner“), 0: gleichwertig, positiv: a kommt nach b.v.compare(a, b) auf – das ist wieder Polymorphie (und das Strategie-Muster, OOP-05).// true, wenn die Liste nach dem Kriterium v
// aufsteigend sortiert ist
methode istAufsteigend<T>(liste : List<T>,
v : Comparator<T>) : boolean
zähle i von 1 bis liste.size() - 1
wenn v.compare(liste.get(i - 1), liste.get(i)) > 0 dann
rückgabe false
Ende wenn
Ende zähle
rückgabe true
Ende methode
Verwendung in Java (Vergleichsfunktion als Lambda-Ausdruck)
List<Buch> buecher = new ArrayList<>();
buecher.add(new Buch("Faust", 1808));
buecher.add(new Buch("Woyzeck", 1879));
Comparator<Buch> nachJahr = (x, y) -> x.getJahr() - y.getJahr();
boolean ok = istAufsteigend(buecher, nachJahr); // true
Das Sortieren mit Vergleichsfunktion (Bubblesort) ist in Block 02 (ALG-05) ausführlich behandelt.
instanceof-Prüfung.> 0 heißt „a gehört hinter b“).List<T> mit übergebener Vergleichsfunktion schreiben.Vorkommen: Teil von „Entwurfsmuster“ (3/9: S22, S23, W24) · W24 T1 A4ba (4 P: zwei Vorteile von Entwurfsmustern), W24 T1 A4bb (2 P: Beispiele für Erzeugungs- und Verhaltensmuster) · S23 T1 A2bb/bc (statische Fabrikmethode im Klassendiagramm modellieren und in Pseudocode implementieren)
Ein Entwurfsmuster (Design Pattern) ist eine bewährte, wiederverwendbare Lösungsvorlage für ein wiederkehrendes Entwurfsproblem der Softwareentwicklung. Es ist kein fertiger Code und keine Bibliothek, sondern eine Idee mit typischer Struktur (beteiligte Klassen, Interfaces und ihre Beziehungen), die auf die konkrete Situation übertragen wird. Bekannt wurde das Konzept durch den Katalog der „Gang of Four“ (Gamma, Helm, Johnson, Vlissides, 1994) mit 23 Mustern. Jedes Muster wird beschrieben durch Name, Problem, Lösung und Konsequenzen (Vor- und Nachteile).
| Vorteil | Erläuterung |
|---|---|
| Bewährte Lösungen | Die Lösung wurde vielfach erprobt; typische Entwurfsfehler werden vermieden, die Qualität steigt. |
| Gemeinsames Vokabular | „Hier nehmen wir einen Observer“ ersetzt eine lange Beschreibung – das erleichtert Kommunikation im Team und Dokumentation. |
| Wartbarkeit und Erweiterbarkeit | Muster entkoppeln Klassen (z. B. kennt das Subjekt nur ein Interface). Erweiterungen sind möglich, ohne bestehenden Code zu ändern. |
| Wiederverwendbarkeit, Zeitersparnis | Der Entwurf muss nicht neu erfunden werden; Lösungen lassen sich auf andere Projekte übertragen. |
| Verständlichkeit | Wer das Muster kennt, versteht fremden Code schneller; neue Teammitglieder arbeiten sich leichter ein. |
Nachteile: zusätzliche Klassen und Indirektion (mehr Komplexität), Gefahr des „Overengineering“ (Muster ohne echtes Problem), Einarbeitungsaufwand, falsch gewählte Muster erschweren die Wartung.
| Kategorie | Leitfrage | Beispiele |
|---|---|---|
| Erzeugungsmuster | Wie werden Objekte erzeugt? Die Erzeugung wird vom Rest des Programms getrennt. | Singleton, Fabrikmethode, Abstrakte Fabrik, Builder (Erbauer), Prototyp |
| Strukturmuster | Wie werden Klassen und Objekte zu größeren Strukturen zusammengesetzt? | Adapter, Fassade, Decorator (Dekorierer), Kompositum, Proxy (Stellvertreter), Brücke, Fliegengewicht |
| Verhaltensmuster | Wie arbeiten Objekte zusammen, wie werden Aufgaben und Algorithmen verteilt? | Observer (Beobachter), Strategie, Schablonenmethode, Befehl (Command), Iterator, Zustand (State), Besucher, Zuständigkeitskette, Vermittler, Memento |
Zweck: Von einer Klasse darf es höchstens ein Objekt geben, und alle Programmteile sollen über einen globalen Zugriffspunkt genau dieses Objekt erhalten. Typische Einsätze: zentrales Protokoll (Logger), Konfiguration, Verbindungspool, Druckwarteschlange.
Aufbau (drei Bausteine):
null),new möglich,getInstance(), die beim ersten Aufruf das Objekt erzeugt („lazy“) und danach immer dasselbe Objekt zurückgibt.Protokoll – statische Elemente unterstrichenclass Protokoll {
private static Protokoll instanz = null;
private Protokoll() {
// z. B. Protokolldatei öffnen
}
public static synchronized Protokoll getInstance() {
if (instanz == null) {
instanz = new Protokoll();
}
return instanz;
}
public void schreibe(String text) {
System.out.println("[LOG] " + text);
}
}
Aufruf überall im Programm: Protokoll.getInstance().schreibe("Start"). Protokoll.getInstance() == Protokoll.getInstance() liefert true.
Thread-Sicherheit: Rufen zwei Threads getInstance() gleichzeitig zum ersten Mal auf, können beide instanz = null sehen und je ein Objekt erzeugen – dann gibt es zwei. Abhilfe: die Methode synchronized deklarieren (nur ein Thread gleichzeitig), das Objekt schon beim Laden der Klasse erzeugen (private static final Protokoll INSTANZ = new Protokoll();, „eager“) oder in Java ein enum mit genau einem Wert verwenden.
Nachteile: globaler Zustand und versteckte Abhängigkeiten (man sieht einer Methode nicht an, dass sie das Singleton benutzt), schlecht testbar (das Objekt lässt sich nicht durch eine Testattrappe ersetzen), Probleme bei Nebenläufigkeit, die Klasse kümmert sich zusätzlich um ihre eigene Erzeugung.
Problem: An vielen Stellen im Code steht eine Fallunterscheidung, die je nach Parameter eine andere konkrete Klasse mit new erzeugt. Kommt eine Klasse hinzu, müssen alle diese Stellen geändert werden, und der aufrufende Code hängt von allen konkreten Klassen ab.
Lösung (statische Fabrikmethode, wie in S23): Eine einzige – meist statische – Methode in der Oberklasse oder in einer eigenen Fabrikklasse entscheidet anhand eines Parameters, welches Objekt erzeugt wird, und gibt es mit dem Typ der Oberklasse bzw. des Interfaces zurück. Der aufrufende Code kennt nur noch die Abstraktion.
Beispiel: Berichte sollen als CSV, JSON oder XML exportiert werden.
erzeugeExporter in der abstrakten Oberklasse (statisch = unterstrichen, hier zusätzlich {static})methode erzeugeExporter(format : string) : Exporter {static}
wenn format = "CSV" dann
rückgabe new CsvExporter()
sonst wenn format = "JSON" dann
rückgabe new JsonExporter()
sonst wenn format = "XML" dann
rückgabe new XmlExporter()
Ende wenn
rückgabe null // unbekanntes Format
Ende methode
// Verwendung – keine Fallunterscheidung mehr:
e : Exporter := Exporter.erzeugeExporter(wahl)
e.exportiere(bericht)
Vorteile: Die Erzeugungslogik steht an einer Stelle (eine neue Klasse = eine Änderung in der Fabrik), der aufrufende Code ist unabhängig von den konkreten Klassen (lose Kopplung), redundante Fallunterscheidungen verschwinden, die Fabrik kann Parameter prüfen und Standardwerte setzen.
Fabrikmethode im Sinne der Gang of Four: Statt einer statischen Methode deklariert eine abstrakte „Erzeuger“-Klasse eine abstrakte Methode erzeugeProdukt(); jede Unterklasse des Erzeugers überschreibt sie und entscheidet so, welches konkrete Produkt entsteht. Die Abstrakte Fabrik erweitert die Idee auf ganze Familien zusammengehöriger Objekte (z. B. alle Bedienelemente im hellen oder dunklen Design). In der Prüfung genügt meist die statische Variante.
Problem: Ein Objekt hat viele, teils optionale Angaben. Konstruktoren mit vielen Parametern sind unlesbar (new Reise("Rom", null, true, false, 2, null)) oder man braucht unzählige überladene Konstruktoren. Lösung: Eine eigene Builder-Klasse sammelt die Angaben Schritt für Schritt über sprechende Methoden und erzeugt mit build() das fertige, vollständig geprüfte Objekt.
Reise r = new Reise.Builder("Rom") // Pflichtangabe
.hotel("Albergo Sole") // optionale Angaben
.personen(2)
.build(); // prüft und erzeugt das Objekt
public-Konstruktor oder nicht statischem getInstance() (dann bräuchte man schon ein Objekt, um das Objekt zu bekommen).getInstance() erzeugt bei jedem Aufruf ein neues Objekt (Prüfung auf null vergessen).: CsvExporter) statt der Oberklasse – dann hängt der Aufrufer wieder von der konkreten Klasse ab.Vorkommen: Teil von „Entwurfsmuster“ (3/9) · Observer: S22 T1 A3 (durchgängig: Beziehung im Klassendiagramm, Interface-Methoden, Realisierung erläutern, Sequenzdiagramm, notifyObservers in Pseudocode – 23 der 30 P), W24 T1 A4bc (4 P: geeignetes Muster für die Aktualisierung aller Anzeigen nennen und begründen)
Problem: Ein Objekt ändert seinen Zustand, und beliebig viele andere Objekte sollen darüber automatisch informiert werden – ohne dass das geänderte Objekt deren konkrete Klassen kennen muss (1:n-Abhängigkeit, lose Kopplung). Signalwörter in der Aufgabe: „sobald sich … ändert, sollen alle Anzeigen aktualisiert werden“, „weitere Anzeigen sollen später hinzukommen“, „App, Display und Verwaltung zeigen denselben Wert“.
| Rolle | Aufgabe | Methoden (englisch / deutsch) |
|---|---|---|
| Subject (Interface oder abstrakte Klasse) | legt fest, wie man sich an- und abmeldet und wie benachrichtigt wird | addObserver(o) / anmelden, removeObserver(o) / abmelden, notifyObservers() / benachrichtigen |
| konkretes Subject | hat den beobachteten Zustand und eine Liste der angemeldeten Observer; ruft nach jeder Änderung notifyObservers() auf | zusätzlich fachliche Setter/Getter |
| Observer (Interface) | Methode, die bei einer Änderung aufgerufen wird | update() / aktualisieren |
| konkrete Observer | implementieren update(), z. B. Anzeige neu zeichnen | – |
Beispiel Kühlhaus: Ein Temperatursensor misst die Temperatur. Bei jeder neuen Messung sollen eine Anzeigetafel und eine Alarmanlage informiert werden; später kommt ein Protokoll dazu.
ObserverPseudocode (Methoden des Temperatursensors)
methode addObserver(o : Observer) : void
observers.add(o)
Ende methode
methode removeObserver(o : Observer) : void
observers.remove(o)
Ende methode
methode notifyObservers() : void
zähle i von 0 bis observers.size() - 1
observers.get(i).update(temperatur)
Ende zähle
Ende methode
methode setTemperatur(t : double) : void
temperatur := t
notifyObservers() // nach jeder Änderung!
Ende methode
Der zeitliche Ablauf: Zuerst melden sich die Beobachter an. Bei einer neuen Messung ruft der Sensor sich selbst notifyObservers() auf; diese Methode ruft nacheinander update(…) bei jedem angemeldeten Objekt auf. Erst wenn alle fertig sind, kehrt der Aufruf zurück.
Push oder Pull? Es gibt zwei Varianten, wie die Beobachter an die neuen Daten kommen:
| Push-Variante | Pull-Variante | |
|---|---|---|
| Signatur | update(temperatur : double) – Daten als Parameter | update() (ohne Daten) |
| Datenfluss | Das Subjekt schickt die geänderten Daten mit. | Der Beobachter holt sich die Daten selbst über Getter beim Subjekt (quelle.getTemperatur()). |
| Referenz auf das Subjekt | nicht nötig | nötig (Attribut im Beobachter, meist im Konstruktor übergeben – dort meldet sich der Beobachter oft auch gleich an) |
| Vorteil | einfach, kein Rückruf nötig, weniger Aufrufe | jeder Beobachter holt nur, was er braucht; Interface bleibt gleich, wenn neue Daten dazukommen |
| Nachteil | alle bekommen alle Daten; ändern sich die Daten, ändert sich das Interface | zusätzliche Aufrufe; Beobachter ist an die konkrete Subjektklasse gebunden |
Vorteile des Observer-Musters: lose Kopplung (das Subjekt kennt nur das Interface), beliebig viele Beobachter zur Laufzeit an- und abmeldbar, neue Beobachter-Klassen ohne Änderung des Subjekts (Erweiterbarkeit), kein ständiges Abfragen (Polling). Nachteile: Reihenfolge der Benachrichtigung nicht festgelegt, Kaskaden von Aktualisierungen, vergessenes Abmelden hält Objekte im Speicher, Ablauf schwerer nachvollziehbar.
notifyObservers() auf, das bei allen angemeldeten Beobachtern update() aufruft. Dadurch werden alle Anzeigen automatisch und gleichzeitig aktualisiert. Weil das Subjekt nur das Observer-Interface kennt, können später weitere Anzeigen ergänzt werden, ohne die Klasse Spielstand zu ändern (lose Kopplung).“Problem: Für eine Aufgabe gibt es mehrere Verfahren (Algorithmen), die austauschbar sein sollen – auch zur Laufzeit – ohne dass die nutzende Klasse lange wenn … sonst wenn …-Ketten enthält. Lösung: Jedes Verfahren wird in eine eigene Klasse gekapselt, die ein gemeinsames Interface realisiert. Die nutzende Klasse (Kontext) hat ein Attribut vom Typ des Interfaces und delegiert die Arbeit an das gerade eingesetzte Strategie-Objekt.
// in der Klasse Navigation (Kontext) methode setStrategie(s : Routenstrategie) : void strategie := s Ende methode methode planeRoute(start : string, ziel : string) : Route rückgabe strategie.berechneRoute(start, ziel) Ende methode
Navigation nav = new Navigation(new SchnellsteRoute());
Route r1 = nav.planeRoute("Kiel", "Lübeck");
nav.setStrategie(new Fahrradroute()); // Wechsel zur Laufzeit
Route r2 = nav.planeRoute("Kiel", "Lübeck");
Strategie statt Unterklassen des Kontexts: Das Verfahren kann zur Laufzeit gewechselt werden; es entstehen keine Unterklassen für jede Kombination (Navigation für Auto mit schnellster Route, Navigation für Fahrrad …); jedes Verfahren ist einzeln test- und wiederverwendbar; neue Verfahren ändern den Kontext nicht. Das Prinzip dahinter: Komposition statt Vererbung. (Ähnlich aufgebaut ist das Zustandsmuster; dort wechselt das Objekt sein Verhalten aber selbst, je nach Zustand.)
Architekturmuster für Anwendungen mit Benutzeroberfläche. Es trennt die Anwendung in drei Teile mit klaren Zuständigkeiten:
Vorteile: Trennung der Zuständigkeiten, Oberfläche austauschbar (Web, App) bei gleichem Model, mehrere Ansichten derselben Daten, parallele Entwicklung im Team, Model ohne Oberfläche testbar.
Problem: Eine vorhandene Klasse (Fremdbibliothek, Altsystem) bietet die benötigte Funktion, aber mit einer anderen Schnittstelle als erwartet, und sie darf oder kann nicht geändert werden. Lösung: Eine Adapter-Klasse realisiert die erwartete Schnittstelle und leitet jeden Aufruf – übersetzt – an das vorhandene Objekt weiter. Beispiel: Die Kühlhaus-Software erwartet Temperaturquelle.getCelsius(), ein neu gekaufter Sensor liefert über seine Bibliothek nur readFahrenheit().
FahrenheitAdapter übersetzt zwischen erwarteter und vorhandener SchnittstelleProblem: Ein Teilsystem besteht aus vielen Klassen, die in einer bestimmten Reihenfolge benutzt werden müssen. Jeder Nutzer müsste alle kennen. Lösung: Eine Fassadenklasse bietet eine einfache, vereinheitlichte Schnittstelle und ruft intern die Klassen des Teilsystems auf. Beispiel: Ein Reiseportal ruft nur ReiseFassade.bucheReise(kunde, angebot) auf; die Fassade reserviert Flug und Hotel, zieht die Zahlung ein und verschickt die Bestätigung.
Abgrenzung: Ein Adapter macht eine vorhandene Schnittstelle passend zu einer erwarteten. Eine Fassade vereinfacht viele Schnittstellen zu einer neuen, einfacheren.
Problem: Objekte sollen zur Laufzeit beliebig kombinierbare Zusatzfunktionen erhalten. Eine Unterklasse für jede Kombination würde zur „Klassenexplosion“ führen. Lösung: Ein Decorator realisiert dieselbe Schnittstelle wie das Objekt, das er „umhüllt“, hält eine Referenz darauf, leitet Aufrufe weiter und ergänzt eigenes Verhalten. Decorator lassen sich beliebig verschachteln. Beispiel Café: new Sirup(new Milch(new Kaffee())) – preis() ergibt 2,50 + 0,30 + 0,50 = 3,30 €. Bekanntes Java-Beispiel: new BufferedReader(new FileReader("daten.txt")).
Zutat ist ein Getraenk und enthält ein Getraenk| Situation in der Aufgabe | Muster | Kategorie |
|---|---|---|
| „Es darf nur ein Objekt geben“, zentrale Konfiguration, globaler Zugriff | Singleton | Erzeugung |
| Je nach Typ oder Parameter das passende Objekt erzeugen; neue Typen sollen leicht hinzukommen | Fabrikmethode | Erzeugung |
| Objekt mit vielen optionalen Angaben schrittweise aufbauen | Builder | Erzeugung |
| Vorhandene oder fremde Klasse passt nicht zur erwarteten Schnittstelle | Adapter | Struktur |
| Komplexes Teilsystem über eine einfache Schnittstelle nutzen | Fassade | Struktur |
| Zusatzfunktionen dynamisch kombinieren, ohne Unterklassen für jede Kombination | Decorator | Struktur |
| Bei einer Änderung sollen alle abhängigen Anzeigen/Objekte automatisch aktualisiert werden | Observer | Verhalten |
| Berechnungsverfahren austauschbar, zur Laufzeit wählbar | Strategie | Verhalten |
| Daten, Darstellung und Steuerung einer Oberfläche trennen | MVC | Architekturmuster |
List<Anzeigetafel>) statt des Interfaces – dann ist die lose Kopplung weg.notifyObservers() nicht aufgerufen, oder update() ändert wieder das Subjekt (Endlosschleife).* oder in falscher Richtung gezeichnet.update() nur beim ersten Beobachter aufgerufen.notifyObservers in Pseudocode (4 P).Enthält nur, was dir auch die IHK beilegen würde: die Notationshilfen (UML-Klassendiagramm und UML-Sequenzdiagramm wie in den Belegsätzen S22/S24, Pseudocode-Konventionen wie S24) und die Anlagen zur Ausgangssituation. Keine Erklärungen.
| Pseudocode | Beschreibung |
|---|---|
wenn [ Bedingung ] dann [ Anweisung ] sonst [ Anweisung ] Ende wenn | Verzweigung |
zähle [ Variable ] von [ Startwert ] bis [ Endwert ] [ Schleifeninhalt ] Ende zähle | Zählschleife (Iteration) |
solange [ Bedingung ] [ Schleifeninhalt ] Ende solange | Kopfgesteuerte Schleife |
wiederhole [ Schleifeninhalt ] solange [ Bedingung ] | Fußgesteuerte Schleife |
:= | Zuweisung |
=; <; >; <=; >=; <> | Vergleiche |
integer double string boolean array | Datentypen |
Die Aufgaben dieses Blocks beziehen sich auf die folgende Ausgangssituation:
Die Kranich Logistik GmbH mit Sitz in Lindhafen ist ein regionaler Paket- und Kurierdienst in Norddeutschland. Sie stellt Briefe, Pakete und Expresssendungen für Online-Händler, Geschäfts- und Privatkunden zu. Zum Unternehmen gehören ein Sortierzentrum mit drei Hallen, 40 Paketstationen mit Fächern in drei Größen sowie ein Fuhrpark aus Lastenrädern, E-Transportern und Kühltransportern.
Die bisherige Software ist über viele Jahre gewachsen und lässt sich kaum noch erweitern. Ihr Ausbildungsbetrieb, die Lorenz Software GmbH, entwickelt deshalb die objektorientierte Plattform „KraniTrack“ (Programmiersprache Java). Sie arbeiten im Entwicklungsteam in folgenden Teilprojekten mit:
Hinweise: Heute ist der 10.10.2026. Für alle Attribute existieren Get-Methoden, sofern nichts anderes angegeben ist. Geldbeträge in Euro, Gewichte in kg. Zeichenketten dürfen im Pseudocode mit = verglichen werden.
| Methode | Beschreibung |
|---|---|
| new List<T>() | erzeugt eine leere Liste für Elemente vom Typ T |
| add(element : T) : void | hängt element am Ende der Liste an |
| get(index : integer) : T | liefert das Element an der Position index (0 ≤ index < size()) |
| size() : integer | liefert die Anzahl der Elemente |
| contains(element : T) : boolean | liefert true, wenn element in der Liste enthalten ist |
| remove(element : T) : boolean | entfernt element aus der Liste |
| Methode | Beschreibung |
|---|---|
| plusTage(tage : integer) : Datum | liefert ein neues Datum, das tage Tage später liegt |
| istVorOderGleich(d : Datum) : boolean | liefert true, wenn das Datum vor d liegt oder gleich d ist |
| Methode | Beschreibung |
|---|---|
| getSendungsNr() : string | liefert die Sendungsnummer, z. B. "KT-1001" |
| getGroesse() : string | liefert die Größenklasse der Sendung: "S", "M" oder "L" |
Etikett (Java)Jedes Versandetikett erhält eine fortlaufende Nummer ab 1000. Das Gewicht muss größer als 0 und höchstens 31,5 kg sein.
class Etikett {
private static int naechsteNr = 1000;
private int nummer;
private String empfaenger;
private double gewicht;
public Etikett(String empfaenger, double gewicht) {
this.nummer = naechsteNr;
naechsteNr = naechsteNr + 1;
this.empfaenger = empfaenger;
this.gewicht = 1.0;
setGewicht(gewicht);
}
public Etikett(String empfaenger) {
this(empfaenger, 1.0);
}
public void setGewicht(double gewicht) {
if (gewicht > 0 && gewicht <= 31.5) {
this.gewicht = gewicht;
}
}
public int getNummer() {
return nummer;
}
public static int getAnzahl() {
return naechsteNr - 1000;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Etikett)) {
return false;
}
Etikett e = (Etikett) o;
return empfaenger.equals(e.empfaenger) && gewicht == e.gewicht;
}
@Override
public int hashCode() {
return empfaenger.hashCode() + Double.hashCode(gewicht);
}
@Override
public String toString() {
return nummer + " | " + empfaenger + " | " + gewicht + " kg";
}
}
PaketfachSendung, Anlage 2) auf. Ein leeres Fach hat keinen Inhalt (null).istFrei() liefert true, wenn keine Sendung im Fach liegt.einlegen(…) erhält eine Sendung und den Abholcode. Die Sendung wird nur eingelegt, wenn das Fach frei ist und die Sendung in das Fach passt (die Größe der Sendung ist kleiner oder gleich der Größe des Fachs, S < M < L). Die Methode meldet zurück, ob das Einlegen geklappt hat.entnehmen(…) erhält einen Abholcode. Stimmt er mit dem gespeicherten Code überein, wird die Sendung zurückgegeben und das Fach geleert (kein Inhalt, kein Abholcode). Ist das Fach leer oder der Code falsch, wird null zurückgegeben.rang(groesse : string) : integer. Sie liefert für "S" den Wert 1, für "M" 2 und für "L" 3.abstract class Fahrzeug {
private String kennung;
protected int reichweiteKm;
public Fahrzeug(String kennung, int reichweiteKm) {
this.kennung = kennung;
this.reichweiteKm = reichweiteKm;
System.out.println("Fahrzeug " + kennung + " angelegt");
}
public String getKennung() {
return kennung;
}
public abstract double kostenProKm();
public final String info() {
return kennung + " (" + reichweiteKm + " km)";
}
}
interface Kuehlbar {
boolean kuehlen(int zielTemperatur);
}
class Lastenrad extends Fahrzeug {
public Lastenrad(String kennung) {
super(kennung, 60);
System.out.println("Lastenrad " + getKennung() + " bereit");
}
@Override
public double kostenProKm() {
return 0.05;
}
}
class Kuehltransporter extends Fahrzeug implements Kuehlbar {
private int temperatur;
public Kuehltransporter(String kennung, int reichweiteKm) {
super(kennung, reichweiteKm);
temperatur = 20;
System.out.println("Kuehlung auf " + temperatur + " Grad");
}
@Override
public double kostenProKm() {
return 0.32;
}
@Override
public boolean kuehlen(int zielTemperatur) {
if (zielTemperatur < -20) {
return false;
}
temperatur = zielTemperatur;
return true;
}
}
PostsendungIn der Sendungsverwaltung gibt es bisher eine einzige Klasse für alle Sendungsarten. Das Attribut typ enthält "BRIEF", "PAKET" oder "EXPRESS".
Alle vier fachlichen Methoden enthalten dieselbe Fallunterscheidung nach typ:
| Methode | "BRIEF" | "PAKET" | "EXPRESS" |
|---|---|---|---|
| berechnePorto() | 0,95 bis einschließlich 0,05 kg, sonst 1,80 | 5,49 bis einschließlich 5 kg, sonst 8,99 | 12,90 + 0,80 je kg Gewicht |
| maxGewicht() | 1,0 | 31,5 | 20,0 |
| istStationsfaehig() | false | true | false |
| berechneVersicherung(warenwert) | 0,0 (Briefe sind nicht versicherbar) | 1 % des Warenwerts, mindestens 2,00 | 0,5 % des Warenwerts |
Künftig sollen weitere Sendungsarten hinzukommen, z. B. Sperrgut und Großbrief.
class Zustellung {
public String beschreibung() {
return "Zustellung";
}
public double gebuehr() {
return 4.00;
}
public String quittung() {
return beschreibung() + ": " + gebuehr() + " EUR";
}
}
class Expresszustellung extends Zustellung {
@Override
public String beschreibung() {
return "Express";
}
@Override
public double gebuehr() {
return super.gebuehr() + 6.00;
}
public String zeitfenster() {
return "bis 12 Uhr";
}
}
class Nachtzustellung extends Expresszustellung {
@Override
public String beschreibung() {
return "Nacht-" + super.beschreibung();
}
}
class Kasse {
public String buche(Zustellung z) {
return "Standardbuchung";
}
public String buche(Expresszustellung e) {
return "Expressbuchung";
}
}
Im Sortierzentrum werden Posten in Regalen und Ablagen zwischengelagert. Dafür wird die generische Klasse Ablage<T> entwickelt; sie soll später auch für andere Elementtypen (z. B. Rollcontainer) verwendet werden.
Ablage<T>: Eine Ablage hat eine Bezeichnung und eine Kapazität (maximale Anzahl Elemente). Die Elemente werden in der Liste elemente gespeichert.Vergleicher<T>: vergleiche(a, b) liefert einen Wert kleiner 0, wenn a kleiner als b ist, 0, wenn beide gleich sind, und einen Wert größer 0, wenn a größer als b ist. GewichtVergleicher vergleicht zwei Lagerposten nach ihrem Gewicht.istFaellig(heute) liefert true, wenn der Posten heute weiterbearbeitet werden muss:
Retoure: wenn seit dem Eingang mindestens 14 Tage vergangen sind (Rücksendung an den Absender),Zollposten: wenn die Zollfreigabe erteilt ist (freigegeben = true),Lagerauftrag: wenn der Abholtermin heute ist oder schon vorbei ist.| postenNr | Klasse | eingang | gewicht | weitere Angabe |
|---|---|---|---|---|
| R-101 | Retoure | 20.09.2026 | 2.4 | – |
| R-102 | Retoure | 01.10.2026 | 0.8 | – |
| Z-201 | Zollposten | 05.10.2026 | 12.5 | freigegeben = true |
| Z-202 | Zollposten | 08.10.2026 | 3.1 | freigegeben = false |
| L-301 | Lagerauftrag | 28.09.2026 | 18.0 | abholtermin = 10.10.2026 |
| L-302 | Lagerauftrag | 02.10.2026 | 7.5 | abholtermin = 12.10.2026 |
Tarifkonfiguration (Entwurf eines Kollegen, Java)Die Tarife stehen in der Datei tarife.properties. Sie soll nur einmal eingelesen werden, und alle Programmteile sollen dieselben Werte verwenden. Die Methode ladeWert liest einen Wert aus der Datei; ihr Rumpf ist hier weggelassen und fehlerfrei.
1 public class Tarifkonfiguration {
2 private Tarifkonfiguration instanz;
3 private double grundpreisPaket;
4 private double kgZuschlag;
5
6 public Tarifkonfiguration() {
7 grundpreisPaket = ladeWert("paket.grundpreis");
8 kgZuschlag = ladeWert("paket.kgzuschlag");
9 }
10
11 public Tarifkonfiguration getInstanz() {
12 instanz = new Tarifkonfiguration();
13 return instanz;
14 }
15
16 public double getGrundpreisPaket() { return grundpreisPaket; }
17 public double getKgZuschlag() { return kgZuschlag; }
18
19 private double ladeWert(String schluessel) { ... } // Rumpf nicht dargestellt
20 }
Empfänger werden über Zustellversuche und eingelegte Sendungen informiert. Im Kundenkonto ist der gewünschte Kanal ("EMAIL", "SMS" oder "PUSH") und die Kontaktangabe (E-Mail-Adresse, Mobilnummer oder Geräte-ID) hinterlegt (Methoden getKanal() : string und getKontakt() : string der Klasse Kunde).
senden(text : string) : boolean, Rückgabe: Erfolg) und kennt ihre Kosten je Nachricht (kostenProNachricht() : double).EmailBenachrichtigung: Kosten 0,00; SmsBenachrichtigung: Kosten 0,07, Texte über 160 Zeichen werden gekürzt; PushBenachrichtigung: Kosten 0,00.Zustellservice benachrichtigt mit der Methode informiereEmpfaenger(k : Kunde, text : string) : double einen Kunden und gibt die Kosten zurück.Bisher steht an fünf Stellen im Programm eine Fallunterscheidung dieser Art:
wenn k.getKanal() = "EMAIL" dann b := new EmailBenachrichtigung(k.getKontakt()) sonst wenn k.getKanal() = "SMS" dann b := new SmsBenachrichtigung(k.getKontakt()) sonst wenn k.getKanal() = "PUSH" dann b := new PushBenachrichtigung(k.getKontakt()) Ende wenn
Der Preis eines Paketauftrags hängt vom Tarif ab. Tarife ändern sich häufig, es kommen Aktionstarife hinzu, und bei einem Auftrag kann der Tarif nachträglich gewechselt werden (z. B. wenn ein Privatkunde ein Geschäftskonto eröffnet).
| Tarif (Klasse) | Preis in Abhängigkeit vom Gewicht g (kg) |
|---|---|
Privattarif | g ≤ 2: 4,99 · 2 < g ≤ 10: 7,49 · g > 10: 11,99 |
Geschaeftstarif | 3,20 + 0,25 je kg (3,20 + 0,25 · g) |
Aktionstarif | pauschal 3,99 |
Die Klasse Paketauftrag speichert das Gewicht und den aktuell gültigen Tarif. Sie hat einen Konstruktor Paketauftrag(gewicht : double, strategie : Preisstrategie), die Methode setStrategie(s : Preisstrategie) : void zum Wechseln des Tarifs und die Methode berechnePreis() : double. Alle Tarifklassen realisieren das Interface Preisstrategie, dessen einzige Methode zu einem Gewicht den Preis liefert.
Jede Sendung hat einen Sendungsstatus (z. B. „Im Sortierzentrum“, „In Zustellung“, „In Paketstation eingelegt“). Sobald sich der Status ändert, sollen alle Anzeigen, die diese Sendung verfolgen, sofort aktualisiert werden: die Kunden-App des Empfängers und die Anzeige in der Halle, in der die Sendung bearbeitet wird. Später sollen weitere Anzeigen (z. B. das Kundenportal im Web) hinzukommen, ohne dass die Klasse Sendungsstatus geändert werden muss.
Für die Umsetzung wird das Observer-Muster in der Pull-Variante verwendet: Ein Observer erhält mit update() nur die Nachricht, dass sich etwas geändert hat, und holt sich den neuen Status selbst beim Subject.
Ablaufbeschreibung:
status der Klasse Sendungsstatus für die Sendung KT-4711.app der Klasse KundenApp und übergibt status an den Konstruktor. Im Konstruktor meldet sich app mit addObserver(this) bei status an.halle der Klasse Hallenanzeige (Halle 3), das sich im Konstruktor bei status anmeldet.setStatus("In Zustellung") bei status auf.setStatus speichert den neuen Status und ruft danach die eigene Methode notifyObservers() auf.notifyObservers() ruft bei allen angemeldeten Observern – in der Reihenfolge der Anmeldung – die Methode update() auf.update() holt sich der Observer mit getStatus() den neuen Status bei status, speichert ihn und ruft danach seine eigene Methode anzeigen() auf.notifyObservers und setStatus zum Client zurück.Alle Aufgaben beziehen sich auf die Ausgangssituation in Anlage 1. Methoden sind – sofern nicht anders angegeben – in Pseudocode nach den Konventionen des Belegsatzes zu entwickeln; eine Programmiersprache (z. B. Java) ist ebenfalls zulässig. Für alle Attribute stehen Get-Methoden zur Verfügung. Summe aller Aufgaben: 178 Punkte (≈ 160 Minuten).
Im Teilprojekt Paketstationen wird die Klasse Paketfach entworfen (Anlage 4). Die Paketstation „Hafenstraße“ hat 24 Fächer.
Paketfach sollen gekapselt werden. Erläutern Sie das Prinzip der Datenkapselung am Beispiel der Klasse Paketfach und nennen Sie zwei Vorteile, die sich daraus für diese Klasse ergeben.Ein Kollege testet die Klasse Etikett (Anlage 3) mit folgendem Programmausschnitt:
Etikett e1 = new Etikett("Ahrens", 2.5);
Etikett e2 = new Etikett("Bode");
Etikett e3 = e1;
e3.setGewicht(4.0);
e2.setGewicht(40.0);
Etikett e4 = new Etikett("Ahrens", 4.0);
Etikett e5 = new Etikett("Cramer", 0.0);
System.out.println(e1); // (1)
System.out.println(e2); // (2)
System.out.println(e1 == e3); // (3)
System.out.println(e1 == e4); // (4)
System.out.println(e1.equals(e4)); // (5)
System.out.println(Etikett.getAnzahl()); // (6)
| Nr. | Ausgabe |
|---|---|
| (1) | |
| (2) | |
| (3) | |
| (4) | |
| (5) | |
| (6) |
e1 und e4 denselben Empfänger und dasselbe Gewicht haben.naechsteNr als static deklariert ist, und beschreiben Sie, was sich ändern würde, wenn es ein normales Attribut wäre.Sie sollen die Klasse Paketfach für KraniTrack entwerfen und implementieren. Die Anforderungen stehen in Anlage 4, die benötigte Klasse Sendung in Anlage 2.
Paketfach mit allen Attributen, Konstruktoren und Methoden einschließlich Datentypen, Parametern und Sichtbarkeiten. Kennzeichnen Sie statische Elemente. Zeichnen Sie außerdem die Klasse Sendung (nur mit Namen) und die Beziehung zwischen beiden Klassen ein.einlegen.entnehmen.Angelehnt an: S26 Teil 2, Aufgabe 2a (Klasse mit vollständigen Signaturen und Sichtbarkeiten) und S24 Teil 1, Aufgabe 3bc (überladener Konstruktor)
| Aussage | abstrakte Klasse | Interface | beide | keines |
|---|---|---|---|---|
1. Mit new können direkt Objekte davon erzeugt werden. | ||||
| 2. Es kann Attribute besitzen, deren Werte sich von Objekt zu Objekt unterscheiden. | ||||
| 3. Eine Klasse kann gleichzeitig mehrere davon erweitern bzw. implementieren. | ||||
| 4. Es kann als Datentyp für Variablen, Parameter und Listen verwendet werden. | ||||
5. Es kann einen Konstruktor besitzen, der von Unterklassen mit super(…) aufgerufen wird. | ||||
| 6. Es kann Methoden ohne Rumpf vorgeben, die jede konkrete Klasse implementieren muss. |
Etikett, Abholschein und Ruecksendeschein sollen über eine einheitliche Methode drucken(drucker : string) : boolean ausgedruckt werden können. Die Klassen haben sonst keine gemeinsamen Attribute; Abholschein erbt bereits von der Klasse Dokument. Entscheiden Sie, ob dafür eine abstrakte Klasse Druckbar oder ein Interface Druckbar verwendet werden sollte, und begründen Sie Ihre Entscheidung mit zwei Argumenten.Angelehnt an: S22 Teil 1, Aufgabe 3bc (Realisierung eines Interfaces erläutern)
Für den Fuhrpark liegen die Klassen aus Anlage 5 vor.
Fahrzeug f1 = new Lastenrad("LR-07");
Fahrzeug f2 = new Kuehltransporter("KT-02", 180);
EScooter ergänzt und eine Testanweisung geschrieben. Der Compiler meldet fünf Fehler. Nennen Sie die fünf Fehler (mit Zeilennummer) und beschreiben Sie jeweils eine Korrektur. 1 public class EScooter extends Fahrzeug {
2 public EScooter(String kennung) {
3 reichweiteKm = 35;
4 System.out.println("E-Scooter bereit");
5 }
6
7 @Override
8 public String info() {
9 return "E-Scooter " + kennung;
10 }
11 }
12
13 // in einer Testklasse:
14 Fahrzeug reserve = new Fahrzeug("RES-1", 50);
| Zeile | Fehler | Korrektur |
|---|---|---|
Kuehltransporter-Objekt sowohl einer Variablen vom Typ Fahrzeug als auch einer Variablen vom Typ Kuehlbar zugewiesen werden kann.Die bisherige Klasse Postsendung (Anlage 6) arbeitet mit dem Attribut typ und enthält in vier Methoden dieselbe Fallunterscheidung. Da weitere Sendungsarten geplant sind, soll die Klasse mithilfe von Vererbung und Polymorphie umgebaut werden.
typ. Tragen Sie Attribute, Konstruktoren und Methoden mit vollständiger Signatur und Sichtbarkeit ein.Postsendung abstrakt und entfernen Sie das Attribut typ.Brief, Paket und Expresssendung.Versicherbar mit der Methode berechneVersicherung modelliert. Es wird nur von den Sendungsarten realisiert, die versichert werden können.Paket mit ihrem Konstruktor und allen Methoden, die sie implementieren muss.Paket erbt von Postsendung und realisiert zusätzlich Versicherbar. Erläutern Sie, warum das in Java möglich ist, eine Klasse aber nicht gleichzeitig von zwei Klassen erben darf.Angelehnt an: S23 Teil 1, Aufgabe 2b (Polymorphie statt redundanter Auswahlstrukturen) und S26 Teil 2, Aufgabe 2a
Zustellung und Expresszustellung (Anlage 7).| Methodenpaar | Überladen / Überschreiben | Begründung |
|---|---|---|
1. Klasse Paketstation: suche(fachNr : integer) : Paketfach und suche(sendungsNr : string) : Paketfach | ||
2. Oberklasse Zustellung: gebuehr() : double;Unterklasse Expresszustellung: gebuehr() : double | ||
3. Oberklasse Fahrzeug: planeTour(km : integer) : boolean;Unterklasse Kuehltransporter: planeTour(km : integer, temperatur : integer) : boolean |
Angelehnt an: S23 Teil 1, Aufgabe 2ba (Polymorphie erläutern)
Für die Abrechnung von Zustellarten liegen die Klassen aus Anlage 7 vor. Gegeben ist folgender Programmausschnitt:
Zustellung a = new Zustellung(); Zustellung b = new Expresszustellung(); Zustellung c = new Nachtzustellung(); Expresszustellung d = new Nachtzustellung(); Kasse k = new Kasse(); System.out.println(b.beschreibung()); // (1) System.out.println(b.quittung()); // (2) System.out.println(c.quittung()); // (3) System.out.println(a.quittung()); // (4) System.out.println(k.buche(b)); // (5) System.out.println(k.buche(d)); // (6)
| Nr. | Ausgabe |
|---|---|
| (1) | |
| (2) | |
| (3) | |
| (4) | |
| (5) | |
| (6) |
b.zeitfenster(); wird vom Compiler abgelehnt, obwohl b auf eine Expresszustellung verweist. Begründen Sie das und geben Sie Anweisungen an, mit denen das Zeitfenster von b sicher ausgegeben werden kann.Expresszustellung x = (Expresszustellung) a; geschieht.Angelehnt an: S26 Teil 2, Aufgabe 2bb (dynamische Bindung)
Im Sortierzentrum werden Posten in Ablagen zwischengelagert. Verwenden Sie die Klassen aus Anlage 8 und die Hilfsklassen aus Anlage 2.
Ablage<T> ist, und nennen Sie einen Vorteil gegenüber einer Klasse, die ihre Elemente als Object speichert.ablegen(element : T) : boolean der Klasse Ablage<T>. Ein Element wird nur abgelegt, wenn die Kapazität noch nicht erreicht ist und das Element noch nicht in der Ablage liegt. Die Methode gibt zurück, ob das Element abgelegt wurde.faelligePosten(ablage : Ablage<Lagerposten>, heute : Datum) : List<Lagerposten> der Klasse Lagerverwaltung. Sie liefert eine neue Liste mit allen Posten der Ablage, die heute fällig sind. Für die Testdaten aus Anlage 8 und heute = 10.10.2026 enthält das Ergebnis die Posten R-101, Z-201 und L-301.faelligePosten wird für jeden Posten dieselbe Anweisung istFaellig(heute) aufgerufen, ausgeführt wird aber je nach Posten eine andere Prüfung. Nennen und erläutern Sie den Mechanismus, der das bewirkt.maximum(v : Vergleicher<T>) : T der Klasse Ablage<T>. Sie liefert das größte Element der Ablage gemäß dem übergebenen Vergleicher, bei einer leeren Ablage null. Für „Regal A“ und einen GewichtVergleicher ist das Ergebnis der Posten L-301.Angelehnt an: W22 Teil 2, Aufgabe 1c/1d (generische Klasse, Vergleichsfunktion) und S26 Teil 2, Aufgabe 2b (polymorpher Aufruf in einer Container-Klasse)
Die Lorenz Software GmbH möchte bei der Entwicklung von KraniTrack konsequent Entwurfsmuster einsetzen.
| Entwurfsmuster | Erzeugungsmuster | Strukturmuster | Verhaltensmuster |
|---|---|---|---|
| Singleton | |||
| Fassade | |||
| Strategie | |||
| Fabrikmethode |
Angelehnt an: W24 Teil 1, Aufgabe 4ba/4bb
Die Klasse Tarifkonfiguration soll nach dem Singleton-Muster umgesetzt werden. Ein Kollege hat einen ersten Entwurf geschrieben (Anlage 9).
| Zeile | Fehler | Korrektur |
|---|---|---|
getInstanz auftreten kann, und nennen Sie eine Lösung.Die Benachrichtigung der Empfänger (Anlage 10) soll überarbeitet werden. Statt der fünf verteilten Fallunterscheidungen soll eine statische Fabrikmethode erzeuge(kanal : string, ziel : string) in der abstrakten Klasse Benachrichtigung das passende Objekt erzeugen.
Benachrichtigung, EmailBenachrichtigung, SmsBenachrichtigung, PushBenachrichtigung und Zustellservice. Tragen Sie die Attribute und Methoden mit vollständiger Signatur und Sichtbarkeit ein und kennzeichnen Sie abstrakte und statische Elemente. Zeichnen Sie die Beziehungen ein (ohne Multiplizitäten).erzeuge. Für einen unbekannten Kanal soll null zurückgegeben werden.Angelehnt an: S23 Teil 1, Aufgabe 2bb/2bc (statische Fabrikmethode modellieren und implementieren)
| Situation | Entwurfsmuster |
|---|---|
1. Die Partner-Spedition liefert Sendungsdaten über ihre Klasse SpeditionApi mit der Methode holeStatusXml(auftragsId). KraniTrack erwartet aber das Interface Statusquelle mit getStatus(sendungsNr) : string. Die Klasse der Spedition darf nicht verändert werden. | |
2. Für eine Einlieferung am Schalter sind fünf Teilsysteme nötig (Waage, Tarif, Etikettendruck, Zahlung, Sendungsverfolgung). Die Schalter-App soll dafür nur eine einzige, einfache Methode einliefern(…) aufrufen. | |
| 3. Zu einer Sendung können Zusatzleistungen (Einschreiben, Nachnahme, Versicherung) beliebig kombiniert werden. Jede erhöht den Preis; für die Kombinationen sollen keine eigenen Unterklassen entstehen. |
Angelehnt an: W24 Teil 1, Aufgabe 4bc (geeignetes Muster nennen)
Die Preisberechnung für Paketaufträge (Anlage 11) wird nach dem Strategie-Muster umgesetzt. Ein Teil des Klassendiagramms liegt vor.
Preisstrategie und alle Beziehungen zwischen den Klassen.preis der Klasse Privattarif und die Methode berechnePreis der Klasse Paketauftrag.Paketauftrag zu bilden (PrivatPaketauftrag, …). Erläutern Sie einen Vorteil des Strategie-Musters gegenüber diesem Vorschlag.Die Sendungsverfolgung (Anlage 12) wird mit dem Observer-Muster umgesetzt.
Sendungsstatus zu den Observern) sowie die fehlenden Methoden in den Klassen Sendungsstatus, KundenApp und Hallenanzeige.Sendungsstatus kennt die Klassen KundenApp und Hallenanzeige nicht, sondern nur das Interface Observer. Erläutern Sie, welchen Vorteil das hat.setStatus(status : string) : void und notifyObservers() : void der Klasse Sendungsstatus sowie die Methode update() : void der Klasse KundenApp.update und der Ablauf ändern würden, wenn statt der Pull- die Push-Variante verwendet wird.Angelehnt an: S22 Teil 1, Aufgabe 3 (Observer: Klassendiagramm, Realisierung, Sequenzdiagramm, notifyObservers) und W24 Teil 1, Aufgabe 4bc
Diese echten Prüfungsaufgaben passen zu diesem Block (Dateien im Ordner Prüfungsvorbereitung). Lösungen gibt es nur für 2023 Sommer, 2023 Winter, 2024 Sommer und 2025 Winter (S23 und W25 vom u-form-Verlag).
| Prüfung | Aufgabe | Thema | Datei · Lösung |
|---|---|---|---|
| Sommer 2022 | T1 A3 (30 P) | Observer durchgängig: Klassendiagramm mit Sichtbarkeiten, Beziehung zum Observer-Interface, Interface-Methoden, Realisierung erläutern, Sequenzdiagramm, notifyObservers in Pseudocode | 2022/sommer/AE 2022 Sommer Teil 1.pdf · keine Lösung |
| Winter 2022 | T2 A1c, A1d (10 P) | generische Klasse erläutern, Sortieren einer List<T> mit Vergleichsfunktion (A1a/b: Bubblesort, Comparator) | 2022/winter/AE 2022 Winter Teil 2.pdf · keine Lösung |
| Sommer 2023 | T1 A2b (22 P) | Polymorphie erläutern, abstrakte Klasse statt redundanter Fallunterscheidungen, statische Fabrikmethode modellieren und implementieren, überschreibende Methoden | 2023/sommer/AE Sommer 2023 Teil 1.pdf · Lösung: AE Sommer 2023 Teil 1+2 Lösungen.pdf |
| Sommer 2024 | T1 A3b (15 P) | Methoden zu einem Klassendiagramm mit Listen; Objekt über überladenen Konstruktor erzeugen (A3bc) | 2024/sommer/AE Sommer 2024 Teil 1+2+3.pdf · Lösung: AE Sommer 2024 Teil 1 Lösungen.pdf |
| Winter 2024 | T1 A4b (10 P) | Vorteile von Entwurfsmustern, Kategorien mit Beispielen, Observer wählen und begründen | 2024/winter/AE Winter 2024 Teil 1.pdf · keine Lösung |
| Sommer 2026 | T2 A2 (20 P) | Klassendiagramm mit abstrakter Klasse, Vererbung und Aggregation; Methode aufgabenHeute in Pseudocode; dynamische Bindung erklären | 2026/sommer/AE Sommer 2026 Teil 2.pdf · keine Lösung |
| Winter 2021 | T1 A3b (7 P) | Klasse mit Attributen und Methoden entwerfen | 2021/winter/AE 2021 Winter Teil 1.pdf · keine Lösung |
| Winter 2024 | T1 A3 (25 P) | Klassendiagramm ergänzen, Aggregation und Komposition, Multiplizitäten (Schwerpunkt Block 03, gute Ergänzung) | 2024/winter/AE Winter 2024 Teil 1.pdf · keine Lösung |