Einleitung: Warum SOLID für C++-Entwickler wichtig ist?
\n\nSOLID ist ein Akronym für fünf grundlegende Prinzipien der objektorientierten Programmierung, formuliert von Robert C. Martin (Onkel Bob). Für einen C++-Entwickler sind diese Prinzipien nicht nur abstrakte Theorie, sondern ein praktisches Werkzeug, das hilft, flexible, wartbare und testbare Anwendungen zu erstellen. In der Welt von C++, in der Speicherverwaltung und Leistung entscheidend sind, hilft die Befolgung von SOLID, "Spaghetti-Code" zu vermeiden und vereinfacht das Refactoring.
\n\nIn diesem Artikel analysieren wir jedes SOLID-Prinzip anhand konkreter Beispiele in C++. Sie werden sehen, wie die Verletzung der Prinzipien zu Problemen führt und wie man sie behebt. Wir verwenden modernes C++ (C++11/14/17/20), damit die Beispiele aktuell sind.
\n\n1. Single Responsibility Principle (SRP) — Prinzip der einzigen Verantwortung
\n\nWesentlicher Inhalt des Prinzips
\nEine Klasse sollte nur einen einzigen Grund für eine Änderung haben. Mit anderen Worten, jede Klasse ist nur für eine Funktionalität oder einen Aspekt des Systems verantwortlich.
\n\nVerletzung des SRP in C++
\nBetrachten wir eine Klasse, die das SRP verletzt: Sie verwaltet gleichzeitig die Daten eines Berichts und dessen Formatierung.
\n\nclass Report {\npublic:\n void generateReport(const std::vector<Data>& data) {\n // Logik zur Datenverarbeitung\n m_data = data;\n }\n \n void saveToFile(const std::string& filename) {\n // Logik zum Speichern in eine Datei\n std::ofstream file(filename);\n for (const auto& item : m_data) {\n file << item.toString() << "\\n";\n }\n }\n \n void printToConsole() {\n // Logik zur Ausgabe auf der Konsole\n for (const auto& item : m_data) {\n std::cout << item.toString() << std::endl;\n }\n }\n \nprivate:\n std::vector<Data> m_data;\n};\n\nProblem: Wenn sich das Ausgabeformat ändert, muss die Klasse Report geändert werden. Wenn sich die Art der Datenspeicherung ändert – ebenfalls. Dies verletzt das SRP.
\n\nBehebung: Verantwortung trennen
\n\n// Nur für die Daten des Berichts verantwortlich\nclass ReportData {\npublic:\n void addData(const Data& item) {\n m_data.push_back(item);\n }\n \n const std::vector<Data>& getData() const {\n return m_data;\n }\n \nprivate:\n std::vector<Data> m_data;\n};\n\n// Nur für das Speichern verantwortlich\nclass FileSaver {\npublic:\n void save(const std::string& filename, const ReportData& report) {\n std::ofstream file(filename);\n for (const auto& item : report.getData()) {\n file << item.toString() << "\\n";\n }\n }\n};\n\n// Nur für die Ausgabe auf der Konsole verantwortlich\nclass ConsolePrinter {\npublic:\n void print(const ReportData& report) {\n for (const auto& item : report.getData()) {\n std::cout << item.toString() << std::endl;\n }\n }\n};\n\nJetzt hat jede Klasse einen einzigen Grund für eine Änderung, was den Code flexibler und leichter testbar macht.
\n\n2. Open-Closed Principle (OCP) — Prinzip der Offenheit/Geschlossenheit
\n\nWesentlicher Inhalt des Prinzips
\nSoftwareentitäten (Klassen, Module, Funktionen) sollten für Erweiterungen offen, aber für Modifikationen geschlossen sein. Das bedeutet, dass Sie in der Lage sein sollten, neue Funktionalität hinzuzufügen, ohne vorhandenen Code zu ändern.
\n\nVerletzung des OCP in C++
\nStellen Sie sich ein System zur Rabattberechnung vor, das für jeden Kundentyp if-else verwendet.
\n\nclass DiscountCalculator {\npublic:\n double calculateDiscount(const std::string& customerType, double amount) {\n if (customerType == "regular") {\n return amount * 0.1; // 10%\n } else if (customerType == "vip") {\n return amount * 0.2; // 20%\n } else if (customerType == "premium") {\n return amount * 0.3; // 30%\n }\n