Entwurfsmuster
Probeklausur SoSe 2023
Sie arbeiten für ein Unternehmen, das sich auf die Herstellung und Programmierung von Robotern spezialisiert hat. Jeder Roboter hat verschiedene Bewegungsmodi, die sich je nach Situation und Umgebung ändern und aktiviert werden können:
- Gehen:
void walk() - Laufen:
void run() - Schwimmen:
void swim()
Ihr Ziel ist es, unter Einsatz objektorientierter Programmierung eine flexible Softwarearchitektur zu entwerfen, die es erlaubt, auch in Zukunft neue Bewegungsmodi zu einem Roboter hinzuzufügen, ohne dass die Roboterklasse geändert werden muss. Darüber hinaus sollte jeder Roboter zur Laufzeit in der Lage sein, seinen Bewegungsmodus zu ändern.
Es ist zu jeden Zeitpunkt immer nur ein Bewegungsmodus aktiv, der mittels der Methode void move(...) der Roboterklasse aufgerufen werden wird.
Teilaufgabe 1:
Welches der aus der Vorlesung bekannten Entwurfsmuster würde diese Anforderungen am besten erfüllen und warum? (Begründung)
Antwort:
Das Strategy-Muster würde diese Anforderungen am besten erfüllen, da es ermöglicht, neue Bewegungsmodi zu einem Roboter hinzuzufügen, ohne dass die Roboterklasse geändert werden muss. Darüber hinaus ermöglicht es, den Bewegungsmodus zur Laufzeit zu ändern.
Teilaufgabe 2:
Skizzieren Sie ein UML-Diagramm für die entsprechende Lösung und erklären Sie kurz die Rolle jeder Klasse in Ihrem Diagramm.
Antwort:

Teilaufgabe 3:
Gehen Sie davon aus, dass die o.g. Methoden für Gehen, Laufen und Schwimmen, gemäß Ihres Architekturvorschlags, bereits implementiert sind.
Geben Sie den C++-Code für die Methode void move(...) der Roboterklasse an.
Antwort:
void move(RobotMode* mode) {
mode->move();
}Entwurfsmuster
Strategy
Mustertyp: Verhaltensmuster
Das Strategy-Muster ist ein Verhaltensmuster, welches es ermöglicht, unterschiedliche Algorithmen für eine bestimmte Aufgabe zu definieren. Es ermöglicht es, neue Algorithmen zu einem bestehenden Objekt hinzuzufügen, ohne dass die Klasse geändert werden muss. Darüber hinaus ermöglicht es, den Algorithmus zur Laufzeit zu ändern.
Verwendungszwecke:
- Sortieralgorithmen
- Routing-Algorithmen
- Bewegungsalgorithmen
UML-Diagramm:

Beispiel Code:
class Strategy {
public:
virtual void execute() = 0;
};
class ConcreteStrategyA : public Strategy {
public:
void execute() override {
std::cout << "ConcreteStrategyA" << std::endl;
}
};
class ConcreteStrategyB : public Strategy {
public:
void execute() override {
std::cout << "ConcreteStrategyB" << std::endl;
}
};
class Context {
private:
Strategy* strategy;
public:
void setStrategy(Strategy* strategy) {
this->strategy = strategy;
}
};Tipps
Muster richtig erkennen
-
Strategy:
Frage: Müssen Algorithmen/Verhaltensweisen (z.B. Bewegungsmodi) zur Laufzeit ausgetauscht werden?
Merkmale:
- Es gibt verschiedene Varianten eines Algorithmus, die unter einer gemeinsamen Schnittstelle implementiert sind.
- Der Kontext hält eine Referenz auf eine abstrakte Strategie.
Hinweis:
- Wenn das Problem darin besteht, dass sich das Verhalten ändern soll, ohne dass die Hauptklasse modifiziert wird, handelt es sich um das Strategy-Muster.
-
Composite:
Frage: Müssen einzelne Objekte und Gruppen von Objekten gleich behandelt werden?
Merkmale:
- Es gibt eine rekursive Struktur (z.B. Baumstruktur) in der sowohl einzelne Objekte als auch Gruppen von Objekten gleich behandelt werden.
- Methoden wie
move(),attack()etc. werden bei Einzelobjekten und Gruppen identisch aufgerufen.
Hinweis:
- Wenn das Problem darin besteht, dass Elemente in einer hierarchischen Struktur verwaltet werden sollen und dieselbe Operation rekursiv auf allen Ebenen ausgeführt werden soll, handelt es sich um das Composite-Muster.
-
Decorator:
Frage: Muss ein Objekt zur Laufzeit um neue Funktionalitäten erweitert werden?
Merkmale:
- Das Muster erlaubt das Umhüllen eines Objekts, sodass neue Verhaltensweisen vor oder nach der ursprünglichen Implementierung ausgeführt werden können.
- Es bietet eine flexible Alternative zur statischen Unterklassenbildung, wenn Erweiterungen erforderlich sind.
Hinweis:
- Wenn die Anforderung lautet, dass die Funktionalität eines Objekts “on the fly” erweitert werden soll, handelt es sich um das Decorator-Muster.
-
Actor:
Frage: Müssen unabhängige Einheiten (Actors) in einem System asynchron Nachrichten austauschen und dabei ihren eigenen Zustand kapseln?
Merkmale:
- Jeder Actor führt seine Aktionen in einer eigenen Ausführungseinheit (Thread oder Task) aus.
- Actors kommunizieren ausschließlich über Nachrichten, ohne direkten Zustandstausch.
- Der interne Zustand eines Actors ist privat und wird nur über die Verarbeitung von Nachrichten verändert.
Hinweis:
- Wenn dein System stark nebenläufig oder verteilt arbeitet und du komplexe Synchronisationsprobleme vermeiden möchtest, ist das Actor-Muster geeignet.
-
Abstract Factory:
Frage: Müssen Familien von verwandten oder abhängigen Objekten erzeugt werden, ohne deren konkrete Klassen explizit zu benennen?
Merkmale:
- Es wird eine Schnittstelle bereitgestellt, über die ganze Produktfamilien erzeugt werden können.
- Alle Produkte, die von einer konkreten Factory erstellt werden, sind aufeinander abgestimmt.
- Das Erzeugungsmuster ist von der Verwendung getrennt, was die Erweiterbarkeit und Austauschbarkeit von Produktfamilien fördert.
Hinweis:
- Wenn dein System flexibel sein soll, sodass unterschiedliche Produktvarianten (z. B. für verschiedene Plattformen oder UI-Themes) leicht austauschbar sind, eignet sich das Abstract Factory-Muster.
-
State:
Frage: Ändert sich das Verhalten eines Objekts in Abhängigkeit von seinem internen Zustand, sodass viele if-/switch-Abfragen notwendig wären?
Merkmale:
- Das Objekt kapselt seinen Zustand in separaten Zustandsklassen.
- Jede Zustandsklasse implementiert das Verhalten, das im jeweiligen Zustand benötigt wird.
- Der Kontext (das Hauptobjekt) hält einen Verweis auf den aktuellen Zustand und delegiert zustandsabhängige Operationen an diesen.
Hinweis:
- Wenn du viele bedingte Anweisungen findest, die das Verhalten in Abhängigkeit vom internen Zustand bestimmen, kann das State-Muster helfen, den Code zu vereinfachen und zu modularisieren.
-
Observer:
Frage: Müssen mehrere Objekte (Beobachter) automatisch benachrichtigt werden, wenn sich der Zustand eines zentralen Objekts (Subjekt) ändert?
Merkmale:
- Das Subjekt hält eine Liste von Beobachtern, die über Zustandsänderungen informiert werden.
- Beobachter registrieren sich beim Subjekt und werden bei Änderungen automatisch benachrichtigt.
- Das Muster ermöglicht eine lose Kopplung zwischen dem Subjekt und seinen Beobachtern.
Hinweis:
- Wenn du ein System entwirfst, in dem mehrere Komponenten (z. B. GUI-Elemente) auf Änderungen in einem zentralen Datenmodell reagieren sollen, ist das Observer-Muster ideal.
-
MVC (Model-View-Controller):
Frage: Muss eine klare Trennung zwischen der Datenhaltung, der Darstellung und der Steuerung (Benutzereingaben) in einer Anwendung realisiert werden?
Merkmale:
- Model: Verwalte die Anwendungsdaten und Geschäftslogik.
- View: Präsentiert die Daten aus dem Model in einer benutzerfreundlichen Form.
- Controller: Vermittelt zwischen Model und View, verarbeitet Benutzereingaben und aktualisiert das Model entsprechend.
Hinweis:
- Wenn du eine interaktive Anwendung (Desktop, Web, Mobile) entwickelst und dabei die Wartbarkeit und Testbarkeit verbessern möchtest, bietet das MVC-Muster eine klare Struktur.
-
Singleton:
Frage: Muss sichergestellt werden, dass es von einer Klasse nur eine einzige Instanz im gesamten System gibt und ein globaler Zugriffspunkt vorhanden ist?
Merkmale:
- Die Instanziierung der Klasse wird kontrolliert, häufig über eine statische Methode wie
getInstance(). - Es wird garantiert, dass nur eine einzige Instanz existiert, die über den gesamten Programmablauf hinweg verwendet wird.
- Das Muster sorgt für einen globalen Zugriffspunkt, ohne auf globale Variablen zurückgreifen zu müssen.
Hinweis:
- Wenn du z. B. einen Konfigurationsmanager, Logger oder Datenbankverbindung implementieren musst, bei denen eine einzige, konsistente Instanz benötigt wird, ist das Singleton-Muster geeignet. Beachte jedoch, dass übermäßiger Einsatz zu versteckten Abhängigkeiten führen kann.
- Die Instanziierung der Klasse wird kontrolliert, häufig über eine statische Methode wie










