Einführung in Entwurfsmuster
Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy. Lektion 90 von 104.
Entwurfsmuster sind bewährte, wiederverwendbare Lösungen für häufige Probleme, die beim Softwareentwurf auftreten. Sie sind kein Code, den du direkt kopierst – sie sind Vorlagen oder Blaupausen, die beschreiben, wie Klassen und Objekte strukturiert werden, um bestimmte Herausforderungen elegant zu lösen.
Das Konzept wurde durch die „Gang of Four“ (GoF) in ihrem 1994 erschienenen Buch populär gemacht, in dem 23 grundlegende Muster katalogisiert wurden. Diese Muster werden typischerweise in drei Kategorien eingeteilt:
| Kategorie | Zweck | Beispiele |
|---|---|---|
| Erzeugungsmuster | Mechanismen zur Objekterzeugung | Singleton, Factory, Builder |
| Strukturmuster | Zusammensetzung von Klassen und Objekten | Adapter, Decorator, Composite |
| Verhaltensmuster | Interaktion und Zuständigkeit von Objekten | Observer, Strategy, Command |
Warum Entwurfsmuster lernen? Sie bieten ein gemeinsames Vokabular unter Entwicklern – wenn man sagt „verwende hier eine Factory“, vermittelt das sofort eine komplexe Idee. Sie helfen dir außerdem, das Rad nicht neu zu erfinden, und machen deinen Code flexibler und wartbarer.
In C++ nutzen Entwurfsmuster die OOP-Funktionen, die du bereits kennengelernt hast: Vererbung, Polymorphie, abstrakte Klassen und Templates. In den nächsten Lektionen wirst du mehrere wesentliche Muster implementieren und verstehen, wann jedes davon das richtige Werkzeug für die jeweilige Aufgabe ist.
Aufgabe
EinfachErstellen wir einen einfachen Musterklassifizierer, der dein Verständnis der drei Kategorien von Entwurfsmustern demonstriert. Du erstellst ein System, das einen Musternamen entgegennimmt und ermittelt, zu welcher Kategorie er gehört – Creational, Structural oder Behavioral – zusammen mit einer kurzen Beschreibung des Zwecks dieser Kategorie.
Du organisierst deinen Code über drei Dateien:
PatternCategory.h: Definiere eine abstrakte Basisklasse namensPatternCategory, die eine Kategorie von Entwurfsmustern repräsentiert. Sie sollte Folgendes enthalten:- Eine rein virtuelle Methode
getName(), die den Namen der Kategorie als String zurückgibt - Eine rein virtuelle Methode
getPurpose(), die eine Beschreibung dessen zurückgibt, was Muster in dieser Kategorie leisten - Einen virtuellen Destruktor
Erstelle anschließend drei abgeleitete Klassen, die von
PatternCategoryerben:CreationalCategory: gibt den Namen"Creational"und den Zweck"Object creation mechanisms"zurückStructuralCategory: gibt den Namen"Structural"und den Zweck"Class and object composition"zurückBehavioralCategory: gibt den Namen"Behavioral"und den Zweck"Object interaction and responsibility"zurück
- Eine rein virtuelle Methode
PatternClassifier.h: Erstelle eine KlassePatternClassifier, die ermitteln kann, zu welcher Kategorie ein bestimmtes Muster gehört.Dein Klassifizierer sollte eine Methode
classifybesitzen, die einen Musternamen (String) entgegennimmt und einen Zeiger auf die entsprechendePatternCategoryzurückgibt. Verwende intelligente Zeiger (std::unique_ptr) für die Speicherverwaltung.Der Klassifizierer sollte diese Muster erkennen:
- Creational:
Singleton,Factory,Builder - Structural:
Adapter,Decorator,Composite - Behavioral:
Observer,Strategy,Command
Wenn der Mustername nicht erkannt wird, gib
nullptrzurück.- Creational:
main.cpp: Lies eine einzelne Eingabe ein: den Namen eines Entwurfsmusters.Erstelle einen
PatternClassifierund verwende ihn, um das eingegebene Muster zu klassifizieren. Wenn das Muster erkannt wird, gib Folgendes aus:[PatternName] is a [CategoryName] pattern Purpose: [CategoryPurpose]Wenn das Muster nicht erkannt wird, gib Folgendes aus:
Unknown pattern: [PatternName]
Beispiel bei der Eingabe Singleton:
Singleton is a Creational pattern
Purpose: Object creation mechanismsBei der Eingabe Observer:
Observer is a Behavioral pattern
Purpose: Object interaction and responsibilityBei der Eingabe Proxy:
Unknown pattern: ProxyDiese Aufgabe festigt die drei Kategorien von Entwurfsmustern und vermittelt gleichzeitig praktische Erfahrung mit Vererbung, Polymorphie und intelligenten Zeigern – alles Konzepte, die du bereits beherrschst. Die Struktur mit mehreren Dateien entspricht der Organisation verwandter Klassen in echten Projekten.
Probier es selbst
#include <iostream>
#include <string>
#include "PatternClassifier.h"
int main() {
// Lies den Pattern-Namen
std::string patternName;
std::cin >> patternName;
// TODO: Erstelle eine PatternClassifier-Instanz
// TODO: Verwende den Classifier, um das Eingabe-Pattern zu klassifizieren
// TODO: Wenn das Pattern erkannt wird, gib aus:
// "[PatternName] is a [CategoryName] pattern"
// "Purpose: [CategoryPurpose]"
// TODO: Wenn das Pattern nicht erkannt wird (nullptr), gib aus:
// "Unknown pattern: [PatternName]"
return 0;
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienC++ Build & KompilierungHeader-Dateien & QuelldateienNamespaces & GültigkeitsbereichEinführung in OOP mit C++Klassen vs. ObjekteDer „this“-ZeigerMethoden (Member-Funktionen)Attribute (Datenmember)Grundlagen zu Ctors & DtorsRückblick – Einfacher Taschenrechner4Klassenattribute
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen und -KlassenZusammenfassung – Bankkontoverwaltung7Vererbung
Grundlagen der VererbungZugriffsebenen bei der VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick – Mitarbeiterhierarchie10STL-Übersicht
STL-Übersicht & PhilosophieSTL-ContainerIteratorenSTL-AlgorithmenFunktoren & Lambda-AusdrückeRückblick – Worthäufigkeit13Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory und Abstract FactoryBuilder-MusterBeobachter-MusterStrategie-Muster2Speicherverwaltung
Stack- vs. Heap-SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Intelligente Zeiger in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsbezeichner in C++Zugriffsbezeichner im DetailInformationsverbergungStruct vs. KlasseVerschachtelte und innere KlassenWiederholung – Studierendenverwaltungssystem8Polymorphie
Polymorphie zur Compilezeit vs. LaufzeitFunktionsüberladungVirtuelle Funktionen erneut betrachtetRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIRückblick – Shape-Calculator3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorMove-KonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktor im DetailDrei-/Fünf-/Null-RegelZusammenfassung – String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladung der Operatoren [] und ()TypkonvertierungsoperatorenWiederholung – Matrix-Klasse9Templates
FunktionstemplatesKlassen-TemplatesTemplate-SpezialisierungVariadische TemplatesGrundlagen zu SFINAE und Type TraitsRückblick – Generischer ContainerÜbe selbstständig: Online-C++-Compiler