Principios SOLID en C++: de la teoría a la práctica
SOLID es un acrónimo de cinco principios fundamentales de la programación orientada a objetos, formulados por Robert Martin (Tío Bob). En el contexto de C++, estos principios ayudan a crear código flexible, mantenible y testeable. En este artículo, analizaremos cada principio con ejemplos prácticos en C++.
1. Single Responsibility Principle (SRP) — Principio de responsabilidad única
Esencia: Cada clase debe tener una única razón para cambiar. En palabras simples: la clase se ocupa de una sola tarea.
Ejemplo incorrecto (violación de SRP):
class Report {public: void generateReport() { /* generación del informe */ } void saveToFile(const std::string& filename) { /* guardar en archivo */ } void printReport() { /* imprimir informe */ }};Aquí la clase Report se encarga de la generación, el guardado y la impresión: tres responsabilidades diferentes.
Ejemplo correcto (cumpliendo SRP):
class Report {public: std::string generate() { /* generación del informe */ return "report data"; }};
class ReportSaver {public: void save(const std::string& data, const std::string& filename) { // guardar en archivo }};
class ReportPrinter {public: void print(const std::string& data) { // imprimir informe }};Ahora cada clase tiene una responsabilidad clara, lo que simplifica las pruebas y la modificación.
2. Open-Closed Principle (OCP) — Principio de abierto/cerrado
Esencia: Las clases deben estar abiertas para la extensión, pero cerradas para la modificación. Debe poder agregar nueva funcionalidad sin cambiar el código existente.
Ejemplo incorrecto:
enum class ShapeType { Circle, Rectangle };
class Shape {public: ShapeType type;};
class AreaCalculator {public: double calculateArea(const Shape& shape) { if (shape.type == ShapeType::Circle) { // cálculo del área del círculo return 3.14 * radius * radius; } else if (shape.type == ShapeType::Rectangle) { // cálculo del área del rectángulo return width * height; } return 0.0; }};Al agregar una nueva figura (por ejemplo, un triángulo), sería necesario modificar AreaCalculator.
Ejemplo correcto (mediante polimorfismo):
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(); }};Ahora, para agregar una nueva figura, basta con crear una nueva clase derivada, sin tocar AreaCalculator.
3. Liskov Substitution Principle (LSP) — Principio de sustitución de Barbara Liskov
Esencia: Los objetos de las subclases deben ser intercambiables con los objetos de la clase base sin alterar la corrección del programa.
Violación clásica (rectángulo-cuadrado):
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