SOLID-Prinzipien in C++: Von der Theorie zur Praxis
SOLID ist ein Akronym für fünf grundlegende Prinzipien der objektorientierten Programmierung, formuliert von Robert C. Martin (Uncle Bob). Im Kontext von C++ helfen diese Prinzipien dabei, flexiblen, wartbaren und testbaren Code zu erstellen. In diesem Artikel analysieren wir jedes Prinzip mit praktischen Beispielen in C++.
1. Single Responsibility Principle (SRP) — Prinzip der einzigen Verantwortung
Kernaussage: Jede Klasse sollte nur einen Grund für eine Änderung haben. Einfach ausgedrückt – die Klasse übernimmt nur eine Aufgabe.
Schlechtes Beispiel (Verletzung des SRP):
class Report {public: void generateReport() { /* Bericht generieren */ } void saveToFile(const std::string& filename) { /* in Datei speichern */ } void printReport() { /* Bericht drucken */ }};Hier ist die Klasse Report für Generierung, Speicherung und Druck verantwortlich – drei verschiedene Verantwortlichkeiten.
Gutes Beispiel (Einhaltung des SRP):
class Report {public: std::string generate() { /* Bericht generieren */ return "report data"; }};
class ReportSaver {public: void save(const std::string& data, const std::string& filename) { // in Datei speichern }};
class ReportPrinter {public: void print(const std::string& data) { // Bericht drucken }};Jetzt hat jede Klasse eine klare Verantwortung, was das Testen und die Modifikation vereinfacht.
2. Open-Closed Principle (OCP) — Prinzip der Offenheit/Geschlossenheit
Kernaussage: Klassen sollten für Erweiterungen offen, aber für Modifikationen geschlossen sein. Sie sollten in der Lage sein, neue Funktionalität hinzuzufügen, ohne vorhandenen Code zu ändern.
Schlechtes Beispiel:
enum class ShapeType { Circle, Rectangle };
class Shape {public: ShapeType type;};
class AreaCalculator {public: double calculateArea(const Shape& shape) { if (shape.type == ShapeType::Circle) { // Berechnung der Kreisfläche return 3.14 * radius * radius; } else if (shape.type == ShapeType::Rectangle) { // Berechnung der Rechtecksfläche return width * height; } return 0.0; }};Beim Hinzufügen einer neuen Form (z.B. Dreieck) müsste AreaCalculator geändert werden.
Gutes Beispiel (durch Polymorphie):
class Shape {public: virtual double area() const = 0; virtual ~Shape() = default;};
class Circle : public Shape { double radius;public: explicit Circle(double r) : radius(r) {} double area() const override { return 3.14159 * radius * radius; }};
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 AreaCalculator {public: double calculateArea(const Shape& shape) { return shape.area(); }};Jetzt reicht es aus, eine neue abgeleitete Klasse zu erstellen, um eine neue Form hinzuzufügen, ohne AreaCalculator zu ändern.
3. Liskov Substitution Principle (LSP) — Prinzip der Substitution von Barbara Liskov
Kernaussage: Objekte von Unterklassen sollten durch Objekte der Basisklasse ersetzbar sein, ohne die Korrektheit des Programms zu beeinträchtigen.
Klassische Verletzung (Rechteck-Quadrat):
class Rectangle {protected: int width, height;public: void setWidth(int w) { width = w; } void setHeight(int h) { height = h; } int area() const { return width * height; }};
class Square : public Rectangle {public: void setWidth(int w) override { width = w; height