SOLID-Prinzipien in C++: Von der Theorie zur Praxis
Die SOLID-Prinzipien sind fünf grundlegende Regeln des objektorientierten Designs, formuliert von Robert Martin. Sie helfen dabei, flexiblen, skalierbaren und leicht wartbaren Code zu erstellen. In diesem Artikel werden wir jedes Prinzip anhand von C++-Beispielen analysieren, von der einfachen Theorie bis hin zu realen praktischen Anwendungsfällen.
1. Single Responsibility Principle (SRP) — Prinzip der einzigen Verantwortung
Theorie: Jede Klasse sollte nur einen einzigen Grund für eine Änderung haben. Mit anderen Worten, die Klasse ist nur für einen Teil der Funktionalität des Programms verantwortlich.
Problem: Oft legen Entwickler die Logik zur Datenverarbeitung, deren Speicherung und die Ausgabe auf dem Bildschirm in eine einzige Klasse. Dies verstößt gegen das SRP.
Praxis in C++: Teilen wir die Klasse Report in drei separate Klassen auf.
// Schlecht: Verstößt gegen SRPclass Report { std::string title; std::string content;public: void generate() { /* Berichtserstellung */ } void saveToFile(const std::string& filename) { /* Speichern in Datei */ } void print() { /* Ausgabe auf Drucker */ }};
// Gut: Einhält SRPclass Report { std::string title; std::string content;public: void generate() { /* nur Erstellung */ }};
class ReportSaver {public: void saveToFile(const Report& report, const std::string& filename) { /* Speichern */ }};
class ReportPrinter {public: void print(const Report& report) { /* Drucken */ }};Fazit: Jede Klasse ist jetzt für ihre eigene Aufgabe verantwortlich. Der Code ist einfacher zu testen und zu ändern.
2. Open-Closed Principle (OCP) — Prinzip der Offenheit/Geschlossenheit
Theorie: Software-Entitäten (Klassen, Module, Funktionen) sollten offen für Erweiterungen, aber geschlossen für Modifikationen sein. Das heißt, wir sollten in der Lage sein, neue Funktionalität hinzuzufügen, ohne vorhandenen Code zu ändern.
Praxis in C++: Verwendung von Polymorphismus und abstrakten Klassen.
#include <iostream>#include <vector>#include <memory>
// Basisklasse für Formenclass Shape {public: virtual double area() const = 0; virtual ~Shape() = default;};
class Rectangle : public Shape { double width, height;public: Rectangle(double w, double h) : width(w), height(h) {} double area() const override { return width * height; }};
class Circle : public Shape { double radius;public: Circle(double r) : radius(r) {} double area() const override { return 3.14159 * radius * radius; }};
// Klasse zur Berechnung der Gesamtfläche — benötigt keine Änderungen beim Hinzufügen neuer Formenclass AreaCalculator {public: double totalArea(const std::vector<std::unique_ptr<Shape>>& shapes) { double total = 0; for (const auto& shape : shapes) { total += shape->area(); } return total; }};
int main() { std::vector<std::unique_ptr<Shape>> shapes; shapes.push_back(std::make_unique<Rectangle>(4, 5)); shapes.push_back(std::make_unique<Circle>(3));
AreaCalculator calc; std::cout << "Total area: " << calc.totalArea(shapes) << std::endl; return 0;}Fazit: Wir können neue Formen (z. B. Triangle) hinzufügen, ohne AreaCalculator zu ändern.
3. Liskov Substitution Principle (LSP) — Prinzip der Substitution von Barbara Liskov
Theorie: Objekte in einem Programm sollten durch Instanzen ihrer Untertypen ersetzbar sein, ohne die Korrektheit des Programms zu beeinträchtigen. Einfach ausgedrückt: Wenn Sie eine Elternklasse und eine Kindklasse haben, sollten Sie das Kind überall dort verwenden können, wo das Elternteil erwartet wird, und alles sollte funktionieren.
Praxis