Einleitung: Warum Entwurfsmuster in C++ wichtig sind
Entwurfsmuster (design patterns) sind bewährte Lösungen für wiederkehrende Probleme, die bei der Softwareentwicklung auftreten. In C++, mit seinem starken Typsystem, RAII und Mehrfachvererbung, gewinnen Muster eine besondere Flexibilität. Sie helfen, den Code lesbarer, wartbarer und skalierbarer zu machen.
In diesem Artikel betrachten wir 4 Schlüsselmuster aus dem GoF-Katalog (Gang of Four): Singleton, Factory Method, Observer und Strategy. Jedes Muster wird mit einem realen Codebeispiel in C++17/20 veranschaulicht.
Erzeugungsmuster: Singleton
Singleton stellt sicher, dass eine Klasse nur eine einzige Instanz hat, und bietet einen globalen Zugriffspunkt darauf. In C++ verwendet der moderne Ansatz eine lokale statische Variable (thread-safe seit C++11).
Beispiel: Anwendungs-Logger
class Logger {public: // Kopieren und Zuweisung löschen Logger(const Logger&) = delete; Logger& operator=(const Logger&) = delete;
static Logger& getInstance() { static Logger instance; // thread-safe seit C++11 return instance; }
void log(const std::string& message) { std::lock_guard<std::mutex> lock(m_mutex); std::cout << "[LOG] " << message << std::endl; }
private: Logger() = default; // privater Konstruktor ~Logger() = default; std::mutex m_mutex;};
// Verwendungint main() { Logger::getInstance().log("Anwendung gestartet"); Logger::getInstance().log("Fehler: Datei nicht gefunden"); return 0;}Vorteile: Garantie der Einzigartigkeit, faules Initialisieren, Threadsicherheit.
Nachteile: Erschwert das Testen (globaler Zustand), verletzt das Prinzip der einzigen Verantwortung.
Erzeugungsmuster: Factory Method
Factory Method definiert eine Schnittstelle zum Erstellen eines Objekts, überlässt es aber den Unterklassen, zu entscheiden, welche Klasse instanziiert wird. In C++ wird es oft durch virtuelle Funktionen und Smart Pointer implementiert.
Beispiel: Generierung verschiedener Dokumenttypen
// Abstraktes Produktclass Document {public: virtual void open() = 0; virtual void save() = 0; virtual ~Document() = default;};
// Konkrete Produkteclass PDFDocument : public Document {public: void open() override { std::cout << "PDF geöffnet"; } void save() override { std::cout << "PDF gespeichert"; }};
class WordDocument : public Document {public: void open() override { std::cout << "Word geöffnet"; } void save() override { std::cout << "Word gespeichert"; }};
// Ersteller (Creator)class Application {public: virtual std::unique_ptr<Document> createDocument() = 0; virtual ~Application() = default;
void newDocument() { auto doc = createDocument(); doc->open(); // Arbeit mit dem Dokument }};
// Konkrete Erstellerclass PDFApplication : public Application {public: std::unique_ptr<Document> createDocument() override { return std::make_unique<PDFDocument>(); }};
class WordApplication : public Application {public: std::unique_ptr<Document> createDocument() override { return std::make_unique<WordDocument>(); }};
// Verwendungint main() { std::unique_ptr<Application> app = std::make_unique<PDFApplication>(); app->newDocument(); // Ausgabe: "PDF geöffnet" return 0;}Dieses Muster ist ideal, wenn die genauen Typen der zu erstellenden Objekte im Voraus nicht bekannt sind.
Verhaltensmuster: Observer
Blogs
Buchempfehlungen
Lesen Sie auch
TypeScript-Sicherheit: SQL-Injection und XSS unter Kontrolle
Asynchrone Programmierung in JavaScript: Vollständiger Leitfaden für Node.js
Wie man Ordnung in ein PHP-Projekt bringt: Hexagonale Architektur ohne Schmerz
Gehälter von C++-Entwicklern in Russland: Marktüberblick 2024
CQRS und Event Sourcing in Go: Architektur für komplexe Systeme