Ü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.“
- 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.
- 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)
| Muster | Kategorie |
|---|---|
| Strategie | |
| Dekorierer | |
| Singleton | |
| Beobachter (Observer) | |
| Proxy | |
| Fabrikmethode (Factory Method) | |
| Zustand (State) | |
| Fassade (Facade) |
Je richtiger Zeile 0,5 Punkte:
| 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:
- 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,RahmenvertragsrabattundAktionsrabatt. (7 Punkte)
Bewertung (7 Punkte):
Aggregation (oder Komposition) zwischen
BestellungundRabattregel, Raute anBestellung(1 Punkt)je Realisierung von
Rabattregeldurch eine der vier Klassen (gestrichelte Linie, hohles Dreieck am Interface) 0,5 Punkte (2 Punkte)je Klasse die Methode
+ berechne(b: Bestellung): Realmit 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.
- Implementiere in Pseudocode die Methode
berechneRabatt()der KlasseBestellung. (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.
- Implementiere in Pseudocode die Methode
berechneder KlasseHaendlerrabatt. 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 €.
- 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 liefertkunde.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.
- Zur Herbstaktion soll die Bestellung
best4711einer 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.
- Jonas schlägt vor, für jede Kombination eine Unterklasse von
Grundradanzulegen, 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.
- Zeichne alle Beziehungen in das UML-Klassendiagramm ein und ergänze die Methoden der Klassen
Regenverdeck,LasergravurundRahmenschloss. (7 Punkte)
Bewertung (7 Punkte):
Realisierung von
RadangebotdurchGrundradund durchOption(je 0,5 Punkte, 1 Punkt)Aggregation zwischen
OptionundRadangebotmit Raute anOption(1,5 Punkte; Komposition ebenfalls richtig)Vererbung von
Optionzu den drei Optionsklassen (durchgezogene Linie, hohles Dreieck anOption) (1,5 Punkte)je Optionsklasse
+ getPreis(): Realund+ 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.
- Implementiere in Pseudocode die Konstruktoren der Klassen
OptionundLasergravur. (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).
- Implementiere in Pseudocode die Methoden
getPreis()undgetBeschreibung()der KlasseLasergravur. 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.
- 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.
- 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.
Regeln beim Prüfen einer Aktivität im Zustand offen (in dieser Reihenfolge):
Ist die Höchstzahl der Teilnehmenden (
max) erreicht, wechselt die Aktivität nach ausgebucht.Sonst: Ist die Mindestzahl (
min) erreicht, wechselt sie nach bestätigt.Sonst: Beginnt sie in höchstens 24 Stunden, wird sie automatisch abgesagt.
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.
- Im Konstruktor der Klasse
Aktivitaetwerden 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).
- Formuliere in Pseudocode die Methode
pruefen(a : Aktivitaet)der KlasseOffenmit 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“.
- Erläutere anhand des gegebenen Klassendiagramms den Begriff Polymorphie. Nutze dazu das Attribut
zustandder KlasseAktivitaet. (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
zustandhat den Typ der abstrakten KlasseAktivitaetszustand. Zur Laufzeit verweist es auf ein Objekt vonOffen,Bestaetigt,Ausgebucht,AbgesagtoderStattgefunden. (1 Punkt)In
Aktivitaet.pruefen()steht nur der eine Aufrufzustand.pruefen(this). Welche der überschriebenenpruefen-Methoden ausgeführt wird, entscheidet sich erst zur Laufzeit anhand des Objekts, auf daszustandgerade verweist (späte bzw. dynamische Bindung). (1 Punkt)Dadurch verhält sich dieselbe Aktivität je nach Zustand anders, ohne dass
Aktivitaeteine Fallunterscheidung über die Zustände enthält. (1 Punkt)
- In jeder Zustandsklasse sind der Konstruktor privat und das Attribut
instanzsowie die MethodegetInstanz()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
newObjekte erzeugt werden. Die statische MethodegetInstanz()erzeugt beim ersten Aufruf das einzige Objekt, speichert es im statischen Attributinstanzund 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)
- 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.
- 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
EchtesProfilsoll die nur klassenintern sichtbaren Instanzvariablenanzeigenameundsteckbrief(je Zeichenkette) sowiehobbys(beliebig viele Zeichenketten) enthalten.Der öffentliche Konstruktor von
EchtesProfilerhält die Benutzer-ID und lädt damit die Daten aus der Datenbank.Der öffentliche Konstruktor von
ProfilProxyerhält die Benutzer-ID des angezeigten Profils und die Benutzer-ID der betrachtenden Person.ProfilProxyundEchtesProfilimplementieren beide das InterfaceProfil.
- Ergänze das UML-Klassendiagramm nach den Vorgaben. Zeichne alle fehlenden Beziehungen ein. (8 Punkte)
Bewertung (8 Punkte):
drei private Instanzvariablen in
EchtesProfil,hobbysmit Multiplizität[0..*](je 0,5 Punkte, 1,5 Punkte)je Konstruktor mit Parametern und Sichtbarkeit
+(2 × 1 Punkt)Methoden
getAnzeigename()undgetSteckbrief()inProfilProxy(1 Punkt) und inEchtesProfil(1 Punkt)Realisierung von
Profildurch beide Klassen (1 Punkt)Aggregation (oder Komposition) zwischen
ProfilProxyundEchtesProfil, 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.
- Implementiere in Pseudocode die Methode
getSteckbrief()der KlasseProfilProxy. 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.
darfSehen()fragt für den Fall „befreundet“ die Datenbank ab. Freundschaften stehen in der Tabellefreundschaft (benutzer_id_1, benutzer_id_2, seit); die kleinere Benutzer-ID steht immer inbenutzer_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 Spalteanzahlliefert. Gib das Ergebnis für den abgebildeten Datenbestand an. (4 Punkte)
| 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:
| 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.
- Die Profilansicht soll Profile nicht mehr selbst mit
new ProfilProxy(...)erzeugen, sondern über eine statische FabrikmethodeerzeugeProfilder KlasseProfilProxy. Implementiere diese Methode in Pseudocode und erläutere, warum ihr RückgabetypProfilund nichtProfilProxysein 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).
- Im Team werden weitere Funktionen geplant. Nenne für jede Situation in der Tabelle ein geeignetes Entwurfsmuster und begründe Deine Wahl. (6 Punkte)
| 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:
| 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).
- 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
AnmeldungFassademit einer Methodeanmelden(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.
Links und weitere Informationen
Lehrartikel: Entwurfsmuster: Strategie, Zustand, Proxy und Dekorierer
zum Nachschlagen: Refactoring.Guru zu Proxy und Dekorierer
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]
