Warum C++ ohne Architektur eine Qual ist
\nWenn Sie jemals ein fremdes (oder Ihr eigenes altes) C++-Projekt geöffnet haben und das Gefühl hatten, dass Ihnen die Augen tränen, weil Tausende von Zeilen in einer einzigen Datei stehen und die Kompilierung zu einem Ritual mit einer Tasse Kaffee wird – Sie sind nicht allein. C++ bietet enorme Freiheit: Sie können Stile mischen, Operatoren überladen und Vorlagen-Magie betreiben. Aber Freiheit ohne Disziplin führt zu Chaos.
\nArchitekturmuster in C++ sind keine langweilige Theorie aus Universitätsvorlesungen. Es sind bewährte Rezepte, die ein Projekt retten, wenn es von einem "Spielzeug" zu einer industriellen Anwendung heranwächst. In diesem Artikel werden wir Architekturmuster in C++ besprechen, die das Leben wirklich einfacher machen: von MVC bis Observer. Und ja, es wird viel Code geben.
\n\nWas ist ein Architekturmuster (und wie unterscheidet es sich von einem "einfachen Muster")
\nViele verwechseln C++-Entwurfsmuster (GoF: Singleton, Factory, Adapter) mit Architekturmustern. Der Unterschied ist einfach:
\n- \n
- Entwurfsmuster sind Mikro-Lösungen auf Klassen- oder Modulebene (z. B. wie man ein Objekt erstellt). \n
- Architekturmuster sind Makro-Lösungen auf der Ebene der gesamten Anwendung (wie Komponenten kommunizieren, wie Schichten getrennt sind). \n
Die Architektur bestimmt, ob Ihr Projekt in sechs Monaten zu einem "großen Schmutzkloß" (Big Ball of Mud) wird. Schauen wir uns daher die drei nützlichsten Muster für C++ an.
\n\n1. MVC (Model-View-Controller) in C++: Teile und herrsche
\nMVC ist ein Klassiker. Es teilt die Anwendung in drei Teile: Daten (Model), Darstellung (View) und Steuerungslogik (Controller). In C++ ist dies besonders relevant für GUI-Anwendungen (Qt, wxWidgets) oder sogar Konsolen-Dienstprogramme.
\n\nWie es im Code aussieht
\nStellen wir uns einen einfachen Taschenrechner vor. Das Model speichert das Ergebnis, die View zeigt es an, der Controller verarbeitet die Eingabe.
\n#include <iostream>\n#include <string>\n\n// Model: weiß nichts über die GUI\nclass CalculatorModel {\npublic:\n double add(double a, double b) {\n result_ = a + b;\n return result_;\n }\n double getResult() const { return result_; }\nprivate:\n double result_ = 0;\n};\n\n// View: nur Darstellung\nclass CalculatorView {\npublic:\n void showResult(double value) {\n std::cout << "Ergebnis: " << value << std::endl;\n }\n void showError(const std::string& msg) {\n std::cerr << "Fehler: " << msg << std::endl;\n }\n};\n\n// Controller: verbindet Model und View\nclass CalculatorController {\npublic:\n CalculatorController(CalculatorModel& model, CalculatorView& view)\n : model_(model), view_(view) {}\n\n void handleAdd(double a, double b) {\n double res = model_.add(a, b);\n view_.showResult(res);\n }\nprivate:\n CalculatorModel& model_;\n CalculatorView& view_;\n};\n\nint main() {\n CalculatorModel model;\n CalculatorView view;\n CalculatorController controller(model, view);\n\n controller.handleAdd(5, 3); // Ausgabe: Ergebnis: 8\n return 0;\n}\nWarum ist das cool? Sie können die Konsolen-View durch ein Qt-Widget ersetzen, ohne das Model anzufassen. Oder Sie können Unit-Tests für das Model hinzufügen, ohne die GUI zu starten. Dies ist die Grundlage für das Design von C++-Anwendungen von Grund auf.
\n\n2. Observer (Beobachter): Wenn Änderungen für alle sichtbar sein sollen
\nStellen Sie sich vor: Sie haben einen Temperatursensor und mehrere Panels, die aktualisiert werden müssen, wenn sich die Temperatur ändert. Anstatt den Sensor jede Sekunde abzufragen (Polling), benachrichtigt der Observer selbst die Abonnenten. Dies ist ein Architekturmuster in C++, das den Code ereignisgesteuert macht.
\n\nImplementierung in C++11 und neuer
\nWir verwenden intelligente Zeiger und std::function für Flexibilität.
\n#include <iostream>\n#include <vector>\n#include <memory>\n#include <functional>\n\n// Schnittstelle des Beobachters\nclass IObserv