Einführung in Entwurfsmuster mit C++
Entwurfsmuster (Design Patterns) sind bewährte architektonische Lösungen für typische Entwicklungsaufgaben. In der Welt von C++ sind sie besonders wichtig, da sie die Erstellung von flexiblem, erweiterbarem und wartbarem Code ermöglichen. In diesem Artikel behandeln wir vier grundlegende Muster: Singleton, Factory Method, Observer und Strategy – mit vollständigen Beispielen in modernem C++ (C++17/20).
Jedes Muster wird von einem praktischen Beispiel, einem Klassendiagramm und Erläuterungen zur Anwendung begleitet. Das Material richtet sich an Entwickler, die bereits mit den Grundlagen von C++ und OOP vertraut sind.
1. Erzeugungsmuster Singleton (Einzelstück)
Singleton stellt sicher, dass eine Klasse nur eine einzige Instanz hat, und bietet einen globalen Zugriffspunkt darauf. In C++ verwendet die klassische Implementierung eine statische Methode und einen privaten Konstruktor.
Wann anwenden
- Verwaltung einer Datenbankverbindung
- Logging-System
- Cache-Manager
- Anwendungskonfiguration
Implementierungsbeispiel (threadsicherer Singleton)
#include <iostream>#include <mutex>#include <memory>
class Logger {private: static std::unique_ptr<Logger> instance; static std::mutex mtx; Logger() { std::cout << "Logger erstellt" << std::endl; } public: Logger(const Logger&) = delete; Logger& operator=(const Logger&) = delete; static Logger* getInstance() { std::lock_guard<std::mutex> lock(mtx); if (!instance) { instance.reset(new Logger()); } return instance.get(); } void log(const std::string& msg) { std::cout << "[LOG] " << msg << std::endl; }};
std::unique_ptr<Logger> Logger::instance = nullptr;std::mutex Logger::mtx;
int main() { Logger* log1 = Logger::getInstance(); Logger* log2 = Logger::getInstance(); log1->log("Testnachricht"); // log1 und log2 zeigen auf dasselbe Objekt std::cout << "log1 == log2: " << (log1 == log2) << std::endl; return 0;}Moderner C++-Ansatz (Meyers' Singleton)
class ConfigManager {public: static ConfigManager& getInstance() { static ConfigManager instance; // threadsicher in C++11+ return instance; } void setParam(const std::string& key, const std::string& value) { params[key] = value; } std::string getParam(const std::string& key) { return params[key]; } private: ConfigManager() = default; std::map<std::string, std::string> params;};2. Erzeugungsmuster Factory Method (Fabrikmethode)
Factory Method definiert eine Schnittstelle zur Erstellung eines Objekts, ermöglicht es jedoch Unterklassen, die konkrete Klasse des zu erstellenden Objekts zu wählen. Dieses Muster ist besonders nützlich, wenn der Objekttyp im Voraus nicht bekannt ist.
Struktur des Musters
- Product – abstrakte Produktklasse
- ConcreteProduct – konkrete Produktimplementierung
- Creator – abstrakte Erzeugerklasse mit Fabrikmethode
- ConcreteCreator – überschreibt die Fabrikmethode
Beispiel: Benachrichtigungssystem
#include <iostream>#include <memory>#include <string>
// Abstraktes Produktclass Notification {public: virtual void send(const std::string& message) = 0; virtual ~Notification() = default;};
// Konkrete Produkteclass EmailNotification : public Notification {public: void send(const std::str