Übungsaufgaben zu Entwurfsmustern: Strategie, Zustand, Proxy und Dekorierer

https://bildung.social/@oerinformatik/

https://oer-informatik.de/entwurfsmuster-uebungsaufgaben

Entwurfsmuster, Strategie, Zustand, Proxy, Dekorierer, Singleton, Polymorphie, UML-Klassendiagramm, Übungsaufgaben, Prüfungsvorbereitung

tl/dr; (Bearbeitungszeit insgesamt 98 min, mehrere Aufgaben): Zwei umfangreiche Übungsaufgaben zu Entwurfsmustern: Rabattregeln eines Onlineshops als Strategie und Ausstattungsoptionen im Fahrradkonfigurator als Dekorierer; Aktivitäten einer Freizeit-App als Zustandsmuster und geschützte Profile als Proxy. Dazu kommen Vorteile und Kategorien von Mustern, ein englischer Fachtext, eine SQL-Abfrage und die Auswahl des passenden Musters. Alle Lösungen sind ausgeblendet und mit Punkteverteilung versehen [AI-Assisted]. (Zuletzt geändert am 28.10.2026)

Diese Übungsaufgaben gehören zum Artikel Entwurfsmuster: Strategie, Zustand, Proxy und Dekorierer. Sie bauen auf den Artikeln Abstrakte Klassen und Interfaces und Polymorphie auf.

Hinweise zur Bearbeitung: Von FIA wird erwartet, dass ihnen Aufgaben auf Profilkompetenz-Ebene (Aufgabe 3.1 und Aufgabe 3.2) keine Probleme bereiten. Zeichne Deine Ergänzungen der Klassendiagramme auf Papier oder mit einem Werkzeug Deiner Wahl, bevor Du die Beispiellösung aufklappst. Gib Attribute und Methoden mit vollständiger Signatur an. Als Datentypen sind UML-Typen (Integer, Real, Boolean, String) ebenso richtig wie die Typen einer Programmiersprache. Für Pseudocode darfst Du eine der vier Notationen JANA, Pseudo-Pascal, Pseudo-Pascal (deutsch) oder Pseudo-Python wählen; er muss erkennbar mit Objekten, Methodenaufrufen und Objekterzeugung (new) arbeiten. Andere fachlich richtige Lösungen sind ebenfalls richtig.

Die Beispiellösungen kannst Du in allen vier Notationen anzeigen; die Notationen sind im Artikel Algorithmen mit Pseudocode dokumentieren beschrieben. Die hier gewählte Notation gilt für alle Codebeispiele der Seite, z. B. für diese Zuweisung:

anzahlRaeder: integer := 7

Aufgabe 3.1: Rabatte und Ausstattung im Fahrrad-Onlineshop (Profilkompetenz)

(ca. 49 min Bearbeitungszeit)

Du bist Auszubildende*r im Entwicklungsteam der CSB-Rad GmbH in Bielefeld. Das Shop-Backend wird gerade neu strukturiert. Cem Sarıkaya, Geschäftsführer Technik und IT, möchte dabei konsequent Entwurfsmuster einsetzen: „Wir haben ständig neue Rabattaktionen und neue Ausstattungsoptionen. Ich will nicht jedes Mal in denselben Klassen herumschreiben.“

  1. Im Team kennen nicht alle Entwurfsmuster. Beschreibe zwei Vorteile, die sich durch die Verwendung von Entwurfsmustern ergeben. (4 Punkte)

Je Vorteil 2 Punkte (Nennung 1 Punkt, Beschreibung 1 Punkt), z. B.:

  • Bewährte Lösungen: Entwurfsmuster sind vielfach erprobte Lösungsschablonen für wiederkehrende Entwurfsprobleme. Man vermeidet typische Fehler und muss keine eigene Lösung erfinden.

  • Gemeinsame Fachsprache: Mit dem Namen eines Musters („Rabattregeln als Strategie“) ist für alle im Team klar, wie die Klassen aufgebaut sind und zusammenarbeiten. Das erleichtert Absprachen, Dokumentation und die Einarbeitung neuer Teammitglieder.

  • Wartbarkeit und Erweiterbarkeit: Veränderliche Teile (z. B. Rabattregeln) sind in eigene Klassen ausgelagert. Neue Varianten werden als neue Klassen ergänzt, ohne bestehende Klassen zu ändern.

  • Lose Kopplung und Testbarkeit: Klassen kennen sich nur über Interfaces. Implementierungen sind austauschbar und lassen sich einzeln testen.

  • Wiederverwendbarkeit: Die kleinen, klar abgegrenzten Klassen lassen sich in anderen Programmteilen wiederverwenden.

  1. Jonas, Dein Mit-Azubi, hat eine Liste von Mustern aus einem Fachbuch abgeschrieben. Ordne jedes Muster in der Tabelle einer der Kategorien Erzeugungs-, Struktur- oder Verhaltensmuster zu. (4 Punkte)
Entwurfsmuster und Kategorien (zum Ausfüllen)
Muster Kategorie
Strategie
Dekorierer
Singleton
Beobachter (Observer)
Proxy
Fabrikmethode (Factory Method)
Zustand (State)
Fassade (Facade)

Je richtiger Zeile 0,5 Punkte:

Lösung: Entwurfsmuster und Kategorien
Muster Kategorie
Strategie Verhaltensmuster
Dekorierer Strukturmuster
Singleton Erzeugungsmuster
Beobachter (Observer) Verhaltensmuster
Proxy Strukturmuster
Fabrikmethode (Factory Method) Erzeugungsmuster
Zustand (State) Verhaltensmuster
Fassade (Facade) Strukturmuster

Für die Rabatte hat Cem folgende Regeln festgelegt (alle Beträge netto):

  • Privatkund*innen erhalten keinen Rabatt.

  • Fachhändler erhalten 4 % auf den Warenwert, ab 5 Fahrrädern in einer Bestellung 8 %.

  • Öffentliche Auftraggeber erhalten laut Rahmenvertrag 6 % auf den Warenwert.

  • Für befristete Aktionen wird ein Aktionsrabatt mit einem frei wählbaren Prozentsatz gesetzt, der die übliche Regel einer Bestellung ersetzt.

Die Rabattregeln ändern sich häufig und sollen deshalb von der Klasse Bestellung entkoppelt werden. Sie werden nach dem Strategie-Entwurfsmuster umgesetzt. Ein erster Entwurf liegt vor:

Vorgabe: Klasse Bestellung, Interface Rabattregel und die Klassen KeinRabatt, Haendlerrabatt, Rahmenvertragsrabatt und Aktionsrabatt ohne Beziehungen (mit KI erstellt)
  1. Vervollständige das UML-Klassendiagramm nach dem Strategie-Entwurfsmuster. Zeichne alle Beziehungen zwischen den Klassen ein und ergänze die Methoden in den Klassen KeinRabatt, Haendlerrabatt, Rahmenvertragsrabatt und Aktionsrabatt. (7 Punkte)
Lösung: Bestellung aggregiert eine Rabattregel; die vier Rabattklassen realisieren das Interface und implementieren berechne (mit KI erstellt)

Bewertung (7 Punkte):

  • Aggregation (oder Komposition) zwischen Bestellung und Rabattregel, Raute an Bestellung (1 Punkt)

  • je Realisierung von Rabattregel durch eine der vier Klassen (gestrichelte Linie, hohles Dreieck am Interface) 0,5 Punkte (2 Punkte)

  • je Klasse die Methode + berechne(b: Bestellung): Real mit vollständiger Signatur (4 × 1 Punkt)

Typische Fehler: Vererbungspfeil (durchgezogen) statt Realisierung; Raute am Interface statt an der Bestellung; die Methode nur im Interface, nicht aber in den realisierenden Klassen eingetragen; Aktionsrabatt vergessen, weil die Klasse schon Einträge hat.

  1. Implementiere in Pseudocode die Methode berechneRabatt() der Klasse Bestellung. (3 Punkte)

Funktion berechneRabatt(): double
    Rückgabe rabattregel.berechne(this)
Ende berechneRabatt

Bewertung: 1 Punkt Rückgabe, 1 Punkt Methodenaufruf über die Referenz rabattregel, 1 Punkt Übergabe der aktuellen Instanz (this bzw. self). Ohne this kann die Rabattregel weder Warenwert noch Anzahl der Räder abfragen.

  1. Implementiere in Pseudocode die Methode berechne der Klasse Haendlerrabatt. Die Methode gibt den Rabatt als Betrag in Euro zurück. (4 Punkte)

Funktion berechne(b: Bestellung): double
    wenn b.getAnzahlRaeder() >= 5 dann
        Rückgabe b.getWarenwert() * 0.08
    sonst
        Rückgabe b.getWarenwert() * 0.04
    Ende wenn
Ende berechne

Bewertung: 1 Punkt Methodenkopf mit Parameter, 1 Punkt Bedingung über b.getAnzahlRaeder() (Grenze „ab 5“, also >= 5), je 1 Punkt für die beiden Rückgaben mit Aufruf von b.getWarenwert().

Kontrolle mit einem Beispiel: Eine Händlerbestellung über 7 Stadträder „Promenade“ zu je 671,43 € hat einen Warenwert von 4.700,01 €; der Rabatt beträgt 8 % davon, also 376,00 €.

  1. Entwirf in Pseudocode den Konstruktor der Klasse Bestellung. Er speichert den übergebenen Kunden, legt eine leere Positionsliste an und setzt die für den Kundentyp passende Rabattregel. Den Kundentyp liefert kunde.getKundentyp() als Zeichenkette "privat", "haendler" oder "oeffentlich". (5 Punkte)

Konstruktor Bestellung(kunde: Kunde)
    this.kunde := kunde
    positionen := new Liste<Bestellposition>()
    typ: string := kunde.getKundentyp()
    wenn typ = "haendler" dann
        rabattregel := new Haendlerrabatt()
    sonst wenn typ = "oeffentlich" dann
        rabattregel := new Rahmenvertragsrabatt()
    sonst
        rabattregel := new KeinRabatt()
    Ende wenn
Ende Bestellung

Bewertung: 1 Punkt Konstruktorkopf, 1 Punkt Zuweisung des Kunden (mit this bzw. self zur Unterscheidung vom Parameter), 1 Punkt leere Liste, 2 Punkte Fallunterscheidung mit Erzeugung der passenden Rabattregel (auch als Mehrfachauswahl oder über setRabattregel(...)).

Hinweis: Die Fallunterscheidung steht hier nur einmal beim Erzeugen. Das ist im Strategiemuster in Ordnung; vermieden werden soll eine Fallunterscheidung bei jeder Berechnung.

  1. Zur Herbstaktion soll die Bestellung best4711 einer Privatkundin abweichend mit 10 % Aktionsrabatt berechnet werden. Stelle den Methodenaufruf in Pseudocode dar, der dafür sorgt. (2 Punkte)

best4711.setRabattregel(new Aktionsrabatt(10.0))

Bewertung: 1 Punkt Aufruf von setRabattregel am Objekt best4711, 1 Punkt Erzeugung eines Aktionsrabatt-Objekts mit dem Prozentsatz als Argument.

Im Fahrradkonfigurator kann die Kundschaft ein Grundrad mit Ausstattungsoptionen kombinieren. Vorerst gibt es drei Optionen (Aufpreise netto): Regenverdeck (289,00 €), Lasergravur eines Wunschtextes am Rahmen (39,00 €) und gleichschließendes Rahmenschloss (41,18 €). Weitere Optionen sollen folgen, und jede Option soll mit jeder anderen kombinierbar sein.

  1. Jonas schlägt vor, für jede Kombination eine Unterklasse von Grundrad anzulegen, z. B. GrundradMitRegenverdeckUndGravur. Erläutere, warum dieser Vorschlag ungünstig ist. (3 Punkte)
  • Die Zahl der Unterklassen wächst exponentiell: Bei 3 Optionen gibt es 2³ = 8 Kombinationen (inklusive „ohne Option“), mit jeder weiteren Option verdoppelt sich die Zahl („Klassenexplosion“). (1 Punkt)

  • Derselbe Code (z. B. der Aufpreis der Gravur) steht in vielen Klassen; eine Preisänderung muss überall nachgezogen werden. (1 Punkt)

  • Die Kombination ist zur Übersetzungszeit festgelegt; die Kundschaft stellt ihre Ausstattung aber erst zur Laufzeit im Konfigurator zusammen. Eine Option mehrfach (z. B. zwei Schlösser) ginge gar nicht. (1 Punkt)

Cem entscheidet sich für das Dekorierer-Entwurfsmuster. Jede Option umhüllt ein Radangebot – ein Grundrad oder ein bereits mit anderen Optionen umhülltes Rad. Dazu müssen die konkreten Optionsklassen die abstrakte Klasse Option erweitern, die das Interface Radangebot implementiert und eine Referenz auf ein Radangebot-Objekt hält.

Vorgabe: Interface Radangebot, Klasse Grundrad, abstrakte Klasse Option sowie Regenverdeck, Lasergravur und Rahmenschloss ohne Beziehungen (mit KI erstellt)
  1. Zeichne alle Beziehungen in das UML-Klassendiagramm ein und ergänze die Methoden der Klassen Regenverdeck, Lasergravur und Rahmenschloss. (7 Punkte)
Lösung: Grundrad und Option realisieren Radangebot, Option aggregiert ein Radangebot, Regenverdeck, Lasergravur und Rahmenschloss erben von Option (mit KI erstellt)

Bewertung (7 Punkte):

  • Realisierung von Radangebot durch Grundrad und durch Option (je 0,5 Punkte, 1 Punkt)

  • Aggregation zwischen Option und Radangebot mit Raute an Option (1,5 Punkte; Komposition ebenfalls richtig)

  • Vererbung von Option zu den drei Optionsklassen (durchgezogene Linie, hohles Dreieck an Option) (1,5 Punkte)

  • je Optionsklasse + getPreis(): Real und + getBeschreibung(): String (3 × 1 Punkt)

Typische Fehler: Die Optionsklassen realisieren das Interface direkt (dann fehlt die gemeinsame Referenz basis); die Raute sitzt am Interface; Option wird mit Grundrad statt mit Radangebot verbunden – dann könnte eine Option kein bereits dekoriertes Rad umhüllen.

  1. Implementiere in Pseudocode die Konstruktoren der Klassen Option und Lasergravur. (4 Punkte)

Konstruktor Option(basis: Radangebot)
    this.basis := basis
Ende Option

Konstruktor Lasergravur(basis: Radangebot, text: string)
    super(basis)
    this.text := text
Ende Lasergravur

Bewertung: je 1 Punkt pro Konstruktorkopf mit Parametern, 1 Punkt Zuweisung in Option, 1 Punkt für super(basis) und Zuweisung von text in Lasergravur (Aufruf des Basisklassenkonstruktors in Worten ist ebenfalls richtig).

  1. Implementiere in Pseudocode die Methoden getPreis() und getBeschreibung() der Klasse Lasergravur. Die Beschreibung ergänzt die Beschreibung des umhüllten Rades um „ + Lasergravur ‚‘“. (4 Punkte)

Funktion getPreis(): double
    Rückgabe basis.getPreis() + 39.00
Ende getPreis

Funktion getBeschreibung(): string
    Rückgabe basis.getBeschreibung() & " + Lasergravur '" & text & "'"
Ende getBeschreibung

Bewertung: je Methode 1 Punkt für den Aufruf beim umhüllten Objekt basis und 1 Punkt für die Ergänzung (Aufpreis bzw. Text) mit Rückgabe.

Typischer Fehler: nur Rückgabe 39.00 – dann ginge der Preis des umhüllten Rades verloren.

  1. Chiamaka Okafor-Schmidt bestellt das Lastenrad „Long John“ (Grundpreis 3.780,67 € netto) mit Regenverdeck und der Gravur „Okafor-Schmidt“. Implementiere in Pseudocode die Erzeugung dieses Radangebots und die Ausgabe des Preises. Gib an, welcher Preis ausgegeben wird. (4 Punkte)

rad: Radangebot := new Grundrad("Lastenrad Long John", 3780.67)
rad := new Regenverdeck(rad)
rad := new Lasergravur(rad, "Okafor-Schmidt")
Ausgabe rad.getPreis()

oder als Einzeiler: rad : Radangebot := new Lasergravur(new Regenverdeck(new Grundrad("Lastenrad Long John", 3780.67)), "Okafor-Schmidt")

Ausgegeben wird 3.780,67 € + 289,00 € + 39,00 € = 4.108,67 € (netto).

Bewertung: 1 Punkt Grundrad erzeugen, 1 Punkt Umhüllung mit beiden Optionen in einer gültigen Reihenfolge, 1 Punkt Referenz vom Typ Radangebot und Ausgabe über getPreis(), 1 Punkt richtiger Preis.

  1. Ein Kollege aus dem Support liest den Quelltext und sagt: „Das ist doch ein Adapter – die Optionen passen das Rad an die Kundenwünsche an.“ Nimm Stellung zu dieser Aussage. (3 Punkte)

Die Aussage ist falsch (1 Punkt). Begründung (2 Punkte), z. B.:

  • Ein Adapter passt eine unpassende Schnittstelle an eine erwartete an: Er realisiert ein anderes Interface als das Objekt, das er umhüllt.

  • Hier realisieren die Optionen dasselbe Interface Radangebot, das sie auch umhüllen, und ergänzen Verhalten (Aufpreis, Beschreibung). Sie lassen sich beliebig verschachteln. Das ist ein Dekorierer. Das alltagssprachliche „anpassen an Kundenwünsche“ hat mit dem Adapter-Muster nichts zu tun.

Aufgabe 3.2: Aktivitäten und geschützte Profile in einer Freizeit-App (Profilkompetenz)

(ca. 49 min Bearbeitungszeit)

Du bist Auszubildende*r bei der Let’s Meet GmbH in Bielefeld. Das Backend der App wird neu entwickelt. Yusuf Demir, Leiter der Entwicklung, hat den Lebenszyklus einer Aktivität als Zustandsdiagramm modelliert. Du sollst ihn zusammen mit Jonas Weber nach dem Zustand-Entwurfsmuster (State Pattern) umsetzen. Yusuf hat dazu das folgende Klassendiagramm und die Regeln für den Zustand „offen“ vorgegeben.

Vorgabe: Klasse Aktivitaet mit Referenz auf die abstrakte Klasse Aktivitaetszustand und die Unterklassen Offen, Bestaetigt, Ausgebucht, Abgesagt und Stattgefunden, jeweils als Singleton (mit KI erstellt)

Regeln beim Prüfen einer Aktivität im Zustand offen (in dieser Reihenfolge):

  1. Ist die Höchstzahl der Teilnehmenden (max) erreicht, wechselt die Aktivität nach ausgebucht.

  2. Sonst: Ist die Mindestzahl (min) erreicht, wechselt sie nach bestätigt.

  3. Sonst: Beginnt sie in höchstens 24 Stunden, wird sie automatisch abgesagt.

  4. In allen anderen Fällen bleibt sie offen.

Die Methode pruefen() der Klasse Aktivitaet wird vom Server alle fünf Minuten und nach jeder An- oder Abmeldung aufgerufen.

  1. Im Konstruktor der Klasse Aktivitaet werden Mindest- und Höchstzahl gespeichert, die Zahl der Angemeldeten auf 0 gesetzt und der Anfangszustand festgelegt: Jede neue Aktivität ist offen. Formuliere den Konstruktor in Pseudocode. (3 Punkte)

Konstruktor Aktivitaet(min: integer, max: integer)
    this.min := min
    this.max := max
    angemeldete := 0
    zustand := Offen.getInstanz()
Ende Aktivitaet

Bewertung: 1 Punkt für die Zuweisungen von min, max und angemeldete, 2 Punkte für die Initialisierung von zustand über die statische Methode Offen.getInstanz().

Typischer Fehler: zustand := new Offen() – der Konstruktor von Offen ist privat und kann außerhalb der Klasse nicht aufgerufen werden (höchstens 1 Punkt).

  1. Formuliere in Pseudocode die Methode pruefen(a : Aktivitaet) der Klasse Offen mit der Kontrollstruktur für die Zustandswechsel nach den Regeln oben. (5 Punkte)

Prozedur pruefen(a: Aktivitaet)
    wenn a.getAngemeldete() >= a.getMax() dann
        a.setZustand(Ausgebucht.getInstanz())
    sonst wenn a.getAngemeldete() >= a.getMin() dann
        a.setZustand(Bestaetigt.getInstanz())
    sonst wenn a.stundenBisBeginn() <= 24 dann
        a.setZustand(Abgesagt.getInstanz())
    Ende wenn
Ende pruefen

Bewertung (5 Punkte): 1 Punkt verschachtelte bzw. mehrstufige Fallunterscheidung in der richtigen Reihenfolge, je 1 Punkt für die drei Bedingungen mit Gettern des übergebenen Objekts a, 1 Punkt für die Zustandswechsel über a.setZustand(...) mit den Singleton-Objekten. Der Fall „bleibt offen“ braucht keine Anweisung.

Beispiel: Lea Schmitts Brettspielabend hat min = 3 und max = 5. Mit 3 Anmeldungen wechselt er beim nächsten Prüfen nach „bestätigt“, mit 5 direkt nach „ausgebucht“.

  1. Erläutere anhand des gegebenen Klassendiagramms den Begriff Polymorphie. Nutze dazu das Attribut zustand der Klasse Aktivitaet. (4 Punkte)
  • Polymorphie (Vielgestaltigkeit) bedeutet, dass eine Referenz eines allgemeinen Typs auf Objekte verschiedener Unterklassen verweisen kann und ein Methodenaufruf je nach tatsächlichem Objekt unterschiedliches Verhalten auslöst. (1 Punkt)

  • Das Attribut zustand hat den Typ der abstrakten Klasse Aktivitaetszustand. Zur Laufzeit verweist es auf ein Objekt von Offen, Bestaetigt, Ausgebucht, Abgesagt oder Stattgefunden. (1 Punkt)

  • In Aktivitaet.pruefen() steht nur der eine Aufruf zustand.pruefen(this). Welche der überschriebenen pruefen-Methoden ausgeführt wird, entscheidet sich erst zur Laufzeit anhand des Objekts, auf das zustand gerade verweist (späte bzw. dynamische Bindung). (1 Punkt)

  • Dadurch verhält sich dieselbe Aktivität je nach Zustand anders, ohne dass Aktivitaet eine Fallunterscheidung über die Zustände enthält. (1 Punkt)

  1. In jeder Zustandsklasse sind der Konstruktor privat und das Attribut instanz sowie die Methode getInstanz() statisch. Beschreibe, was dadurch erreicht wird und warum das für Zustandsobjekte sinnvoll ist. (3 Punkte)
  • Der private Konstruktor verhindert, dass außerhalb der Klasse mit new Objekte erzeugt werden. Die statische Methode getInstanz() erzeugt beim ersten Aufruf das einzige Objekt, speichert es im statischen Attribut instanz und gibt bei jedem weiteren Aufruf dieses Objekt zurück. (1 Punkt)

  • Von jeder Zustandsklasse gibt es also genau ein Objekt (Entwurfsmuster Singleton, ein Erzeugungsmuster). (1 Punkt)

  • Sinnvoll, weil die Zustandsobjekte keine eigenen Daten speichern – alle Daten stehen in der Aktivitaet. Tausende Aktivitäten können sich dieselben fünf Zustandsobjekte teilen; bei jedem Zustandswechsel muss kein neues Objekt erzeugt werden (Speicher, Laufzeit). (1 Punkt)

  1. Jonas wundert sich: „Das Klassendiagramm sieht genauso aus wie bei unserer Strategie für die Push-Benachrichtigungen. Ist das nicht dasselbe Muster?“ Beschreibe zwei Unterschiede zwischen dem Zustand- und dem Strategie-Entwurfsmuster. (3 Punkte)

Zwei Unterschiede, z. B. (1,5 Punkte je Unterschied):

  • Zweck: Eine Strategie kapselt einen austauschbaren Algorithmus für dieselbe Aufgabe; ein Zustand kapselt das gesamte Verhalten eines Objekts in einer bestimmten Situation seines Lebenszyklus.

  • Wer wechselt: Die Strategie wird meist von außen gesetzt (Client, Konstruktor, Setter). Beim Zustandsmuster wechseln die Zustandsobjekte selbst den Zustand des Kontexts (a.setZustand(...)), wenn ein Übergang aus dem Zustandsdiagramm eintritt.

  • Abhängigkeiten: Konkrete Strategien kennen einander nicht. Konkrete Zustände kennen ihre Folgezustände.

Minderjährige Nutzende (16 und 17 Jahre) dürfen ihr Profil nur Kontakten zeigen; Volljährige können das ebenfalls einstellen (Spalte profil_sichtbar in der Tabelle benutzer). Bisher prüft jede Bildschirmansicht selbst, ob sie den Steckbrief eines Profils (Alter, Wohnort, Hobbys) anzeigen darf. Dr. Marta Kowalczyk, die Datenschutzbeauftragte, hat dabei schon eine Lücke gefunden. Yusuf möchte die Prüfung deshalb an einer Stelle bündeln und dafür das Proxy-Muster einsetzen. Bei der Recherche hat er folgende Beschreibung gefunden:

The Proxy pattern places a stand-in object between a client and a real object. Because the proxy implements the same interface as the real object, the client cannot tell them apart and does not need to be changed. A protection proxy checks whether the caller is allowed to use a method before it forwards the request to the real object. A virtual proxy postpones the creation of an expensive object until it is actually needed. Unlike a decorator, a proxy usually controls the lifecycle of its real object and does not add new features for the client.

  1. Werte den englischen Text aus.

fa) Erläutere zwei Aspekte des Proxy-Musters, die im Text genannt werden. (4 Punkte)

fb) Der geplante ProfilProxy soll den Steckbrief nur Berechtigten zeigen und das echte Profil erst dann aus der Datenbank laden, wenn es wirklich gebraucht wird. Nenne die beiden Arten von Proxys aus dem Text, die er damit vereint. (2 Punkte)

fa) Zwei Aspekte, je 2 Punkte, z. B.:

  • Stellvertreter mit gleicher Schnittstelle: Ein Proxy steht als Stellvertreter zwischen Client und echtem Objekt. Weil er dasselbe Interface implementiert, kann der Client ihn nicht vom echten Objekt unterscheiden und muss nicht geändert werden.

  • Schutzproxy: Er prüft vor dem Weiterleiten, ob der Aufrufer die Methode nutzen darf, und leitet den Aufruf nur dann an das echte Objekt weiter.

  • Virtueller Proxy: Er erzeugt ein aufwendiges Objekt erst, wenn es tatsächlich benötigt wird (verzögerte Erzeugung, lazy initialization).

  • Abgrenzung zum Dekorierer: Ein Proxy verwaltet meist selbst den Lebenszyklus des echten Objekts (erzeugt es) und fügt für den Client keine neuen Funktionen hinzu; ein Dekorierer ergänzt Funktionen.

fb) Schutzproxy (protection proxy) und virtueller Proxy (virtual proxy) (je 1 Punkt).

Yusuf hat mit dem Klassendiagramm begonnen. Für den Proxy gelten folgende Vorgaben:

  • Die Klasse EchtesProfil soll die nur klassenintern sichtbaren Instanzvariablen anzeigename und steckbrief (je Zeichenkette) sowie hobbys (beliebig viele Zeichenketten) enthalten.

  • Der öffentliche Konstruktor von EchtesProfil erhält die Benutzer-ID und lädt damit die Daten aus der Datenbank.

  • Der öffentliche Konstruktor von ProfilProxy erhält die Benutzer-ID des angezeigten Profils und die Benutzer-ID der betrachtenden Person.

  • ProfilProxy und EchtesProfil implementieren beide das Interface Profil.

Vorgabe: Klasse Profilansicht mit Assoziation zum Interface Profil, darunter ProfilProxy mit Attributen und darfSehen sowie EchtesProfil mit getHobbys, ohne Beziehungen (mit KI erstellt)
  1. Ergänze das UML-Klassendiagramm nach den Vorgaben. Zeichne alle fehlenden Beziehungen ein. (8 Punkte)
Lösung: ProfilProxy und EchtesProfil realisieren Profil; ProfilProxy aggregiert EchtesProfil (mit KI erstellt)

Bewertung (8 Punkte):

  • drei private Instanzvariablen in EchtesProfil, hobbys mit Multiplizität [0..*] (je 0,5 Punkte, 1,5 Punkte)

  • je Konstruktor mit Parametern und Sichtbarkeit + (2 × 1 Punkt)

  • Methoden getAnzeigename() und getSteckbrief() in ProfilProxy (1 Punkt) und in EchtesProfil (1 Punkt)

  • Realisierung von Profil durch beide Klassen (1 Punkt)

  • Aggregation (oder Komposition) zwischen ProfilProxy und EchtesProfil, Raute am Proxy (1,5 Punkte)

Der Steckbrief darf angezeigt werden, wenn die betrachtende Person das Profil selbst besitzt oder wenn das Profil für alle sichtbar ist oder wenn beide befreundet sind. Die private Methode darfSehen() prüft das; Du kannst sie verwenden.

  1. Implementiere in Pseudocode die Methode getSteckbrief() der Klasse ProfilProxy. Darf die betrachtende Person den Steckbrief nicht sehen, wird der Text „Dieses Profil ist nur für Kontakte sichtbar.“ zurückgegeben. Das echte Profil soll erst erzeugt werden, wenn es zum ersten Mal gebraucht wird. (5 Punkte)

Funktion getSteckbrief(): string
    wenn nicht darfSehen() dann
        Rückgabe "Dieses Profil ist nur für Kontakte sichtbar."
    Ende wenn
    wenn echtesProfil = null dann
        echtesProfil := new EchtesProfil(benutzerId)
    Ende wenn
    Rückgabe echtesProfil.getSteckbrief()
Ende getSteckbrief

Bewertung (5 Punkte): 1 Punkt Prüfung über darfSehen(), 1 Punkt Ersatztext als Rückgabe, 1 Punkt Prüfung auf null, 1 Punkt Erzeugung von EchtesProfil mit der Benutzer-ID, 1 Punkt Weiterleitung an echtesProfil.getSteckbrief() mit Rückgabe.

Typischer Fehler: Das echte Profil wird vor der Berechtigungsprüfung geladen – dann gehen personenbezogene Daten unnötig über die Leitung (Datenminimierung) und der Vorteil des virtuellen Proxys ist verloren.

  1. darfSehen() fragt für den Fall „befreundet“ die Datenbank ab. Freundschaften stehen in der Tabelle freundschaft (benutzer_id_1, benutzer_id_2, seit); die kleinere Benutzer-ID steht immer in benutzer_id_1. Formuliere eine SQL-Abfrage, die für die betrachtende Person Lea Schmitt (Benutzer-ID 3) und das Profil von Noah Bergmann (Benutzer-ID 13) die Anzahl der Freundschaften zwischen beiden als Spalte anzahl liefert. Gib das Ergebnis für den abgebildeten Datenbestand an. (4 Punkte)
Auszug aus der Tabelle freundschaft
benutzer_id_1 benutzer_id_2 seit
1 6 2025-05-11
1 10 2026-10-05
2 12 2026-10-18
3 5 2025-03-02
3 9 2025-10-20
5 7 2026-01-15

SELECT COUNT(*) AS anzahl
FROM freundschaft
WHERE benutzer_id_1 = LEAST(3, 13)
  AND benutzer_id_2 = GREATEST(3, 13);

oder ohne LEAST/GREATEST (Standard-SQL, in allen DBMS gleich):

SELECT COUNT(*) AS anzahl
FROM freundschaft
WHERE (benutzer_id_1 = 3 AND benutzer_id_2 = 13)
   OR (benutzer_id_1 = 13 AND benutzer_id_2 = 3);

Ergebnis:

Ergebnis der Abfrage für Lea Schmitt und Noah Bergmann
anzahl
0

Lea und Noah sind nicht befreundet; Noahs Steckbrief bleibt für Lea verborgen. Zur Kontrolle: Für Lea und Kim Bauer (Benutzer-ID 5) liefert dieselbe Abfrage 1.

Bewertung (4 Punkte): 1 Punkt COUNT(*) mit Alias, 2 Punkte Bedingung, die die Reihenfolge der IDs berücksichtigt (nur benutzer_id_1 = 13 AND benutzer_id_2 = 3 wäre falsch), 1 Punkt richtiges Ergebnis.

  1. Die Profilansicht soll Profile nicht mehr selbst mit new ProfilProxy(...) erzeugen, sondern über eine statische Fabrikmethode erzeugeProfil der Klasse ProfilProxy. Implementiere diese Methode in Pseudocode und erläutere, warum ihr Rückgabetyp Profil und nicht ProfilProxy sein sollte. (4 Punkte)

statisch Funktion erzeugeProfil(benutzerId: integer, betrachterId: integer): Profil
    Rückgabe new ProfilProxy(benutzerId, betrachterId)
Ende erzeugeProfil

Bewertung: 1 Punkt Eigenschaft static, 0,5 Punkte je Übergabeparameter (1 Punkt), 1 Punkt Rückgabe mit Objekterzeugung.

Erläuterung (1 Punkt): Der Client soll nur das Interface Profil kennen. So kann die Fabrikmethode später auch andere Implementierungen liefern (z. B. für die eigene Person direkt ein EchtesProfil), ohne dass die Profilansicht geändert werden muss (lose Kopplung).

  1. Im Team werden weitere Funktionen geplant. Nenne für jede Situation in der Tabelle ein geeignetes Entwurfsmuster und begründe Deine Wahl. (6 Punkte)
Situationen für geplante Funktionen (zum Ausfüllen)
Situation Muster Begründung
Wird eine Aktivität abgesagt, sollen die App-Anzeige, der E-Mail-Dienst und das Moderationsdashboard automatisch informiert werden. Weitere Empfänger sollen später leicht hinzukommen.
Für die Altersverifizierung soll ein zugekaufter Dienst eingebunden werden. Er bietet die Methode checkIdentity(xml) an, das Backend erwartet aber das vorhandene Interface Verifizierer mit pruefeAlter(benutzer).
Je nach Art einer Aktivität (Sport, Kultur, Essen …) soll eine passend gestaltete Teilnahmebestätigung erzeugt werden. Neue Arten sollen ergänzt werden können, ohne den Code zu ändern, der die Bestätigungen anfordert.

Je Zeile 1 Punkt für das Muster, 1 Punkt für die Begründung:

Lösung: Situationen und passende Entwurfsmuster
Situation Muster Begründung
Absage an mehrere Empfänger Beobachter (Observer) Die Aktivität (Subjekt) führt eine Liste angemeldeter Beobachter und benachrichtigt bei einer Zustandsänderung alle über ein gemeinsames Interface. Neue Empfänger melden sich nur an; die Aktivität bleibt unverändert (lose Kopplung).
Zugekaufter Verifizierungsdienst Adapter Eine Adapterklasse realisiert das erwartete Interface Verifizierer und übersetzt pruefeAlter(benutzer) in einen Aufruf von checkIdentity(xml). Weder Backend noch Dienst müssen geändert werden.
Teilnahmebestätigungen je Aktivitätsart Fabrikmethode (Factory Method) Die Erzeugung der Bestätigung steht in einer Methode, die Unterklassen je Aktivitätsart überschreiben. Der aufrufende Code arbeitet nur mit dem gemeinsamen Interface der Bestätigungen; neue Arten erfordern nur neue Unterklassen.

Andere begründete Lösungen sind möglich (z. B. Strategie für die Gestaltung der Bestätigung, wenn die Begründung passt).

  1. Nenne ein weiteres Entwurfsmuster, das in dieser Aufgabe noch nicht vorkommt, und beschreibe es. (3 Punkte)

1 Punkt für ein passendes Muster, 2 Punkte für die Beschreibung (Zweck und Aufbau), z. B.:

  • Fassade (Strukturmuster): Eine Klasse bietet eine einfache Schnittstelle zu einem komplexen Teilsystem an. Sie nimmt Aufrufe entgegen und verteilt sie an die vielen Klassen des Teilsystems, z. B. eine Klasse AnmeldungFassade mit einer Methode anmelden(benutzer, aktivitaet), die Altersprüfung, Teilnahme, Warteliste und Benachrichtigung koordiniert.

  • Kompositum (Strukturmuster): Einzelobjekte und Gruppen von Objekten werden über ein gemeinsames Interface gleich behandelt (Baumstruktur), z. B. Hobbys und Hobbykategorien.

  • Abstrakte Fabrik, Erbauer, Befehl, Iterator, Schablonenmethode … mit passender Beschreibung.

Nicht gewertet werden Zustand, Strategie, Dekorierer, Singleton, Proxy, Beobachter, Adapter und Fabrikmethode, da sie in der Aufgabe bereits vorkommen.


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 Entwurfsmustern: Strategie, Zustand, Proxy und Dekorierer” von oer-informatik.de (H. Stein), Lizenz: CC BY 4.0. Der Artikel wurde unter https://oer-informatik.de/entwurfsmuster-uebungsaufgaben veröffentlicht, die Quelltexte sind in weiterverarbeitbarer Form verfügbar im Repository unter https://gitlab.com/oer-informatik/design-pattern/entwurfsmuster. Erstellt mit KI-Assistenz; von mir umfassend redaktionell überarbeitet, geprüft und freigegeben. Stand: 28.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: