Principios SOLID en C++: de la teoría a la práctica
Los principios SOLID son cinco reglas fundamentales del diseño orientado a objetos, formuladas por Robert Martin. Ayudan a crear código flexible, escalable y fácil de mantener. En este artículo, analizaremos cada principio con ejemplos en C++, desde la teoría simple hasta casos prácticos reales.
1. Single Responsibility Principle (SRP) — Principio de responsabilidad única
Teoría: Cada clase debe tener solo una razón para cambiar. En otras palabras, la clase es responsable de una sola parte de la funcionalidad del programa.
Problema: A menudo, los desarrolladores colocan en una sola clase la lógica de trabajo con datos, su almacenamiento y su visualización en pantalla. Esto viola el SRP.
Práctica en C++: Dividamos la clase Report en tres clases separadas.
// Mal: viola SRPclass Report { std::string title; std::string content;public: void generate() { /* generación del reporte */ } void saveToFile(const std::string& filename) { /* guardar en archivo */ } void print() { /* imprimir */ }};
// Bien: cumple SRPclass Report { std::string title; std::string content;public: void generate() { /* solo generación */ }};
class ReportSaver {public: void saveToFile(const Report& report, const std::string& filename) { /* guardar */ }};
class ReportPrinter {public: void print(const Report& report) { /* imprimir */ }};Conclusión: Ahora cada clase es responsable de su propia tarea. El código se ha vuelto más fácil de probar y modificar.
2. Open-Closed Principle (OCP) — Principio de abierto/cerrado
Teoría: Las entidades de software (clases, módulos, funciones) deben estar abiertas para la extensión, pero cerradas para la modificación. Es decir, debemos poder agregar nueva funcionalidad sin cambiar el código existente.
Práctica en C++: Usamos polimorfismo y clases abstractas.
#include <iostream>#include <vector>#include <memory>
// Clase base para figurasclass 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; }};
// Clase para calcular el área total — no requiere cambios al agregar nuevas figurasclass 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;}Conclusión: Podemos agregar nuevas figuras (por ejemplo, Triangle) sin tocar AreaCalculator.
3. Liskov Substitution Principle (LSP) — Principio de sustitución de Barbara Liskov
Teoría: Los objetos en un programa deben ser reemplazables por instancias de sus subtipos sin alterar la corrección del programa. En términos simples, si tienes una clase padre y una clase hija, debes poder usar la hija donde se espera la padre, y todo debe funcionar.
Práctica