Principios SOLID en C++: teoría y práctica de la A a la Z
SOLID es un acrónimo que designa cinco principios fundamentales de la programación y el diseño orientados a objetos, formulados por Robert Martin (también conocido como "Tío Bob"). Para C++, estos principios son especialmente importantes, ya que permiten crear código flexible, mantenible y comprobable que es fácil de escalar. En este artículo, analizaremos cada principio desde un punto de vista teórico y lo respaldaremos con ejemplos prácticos en C++.
¿Qué es SOLID y por qué es necesario?
SOLID es un conjunto de reglas que ayudan al desarrollador a evitar el código "malo". El cumplimiento de estos principios conduce a una reducción del acoplamiento (coupling) y a un aumento de la cohesión (cohesion) de los módulos. En C++, donde las potentes capacidades de las plantillas y la herencia múltiple pueden llevar fácilmente al caos, SOLID sirve como una brújula confiable. Cada letra del acrónimo se descifra de la siguiente manera:
- S — Single Responsibility Principle (Principio de Responsabilidad Única)
- O — Open-Closed Principle (Principio Abierto-Cerrado)
- L — Liskov Substitution Principle (Principio de Sustitución de Liskov)
- I — Interface Segregation Principle (Principio de Segregación de Interfaces)
- D — Dependency Inversion Principle (Principio de Inversión de Dependencias)
Principio de Responsabilidad Única (Single Responsibility)
Teoría: Una clase debe tener solo una razón para cambiar. En otras palabras, una clase debe ser responsable de una sola parte de la funcionalidad del programa.
Práctica en C++: La violación de este principio a menudo conduce a "objetos divinos" que hacen todo a la vez.
// ❌ Mal: la clase viola SRPclass Report {public: void generateReport() { /* generación de datos */ } void saveToFile(const std::string& filename) { /* escritura en archivo */ } void printReport() { /* envío a impresora */ }};
// ✅ Bien: separamos las responsabilidadesclass ReportData {public: std::string generate() { return "Report data"; }};
class ReportSaver {public: void save(const std::string& data, const std::string& filename) { std::ofstream file(filename); file << data; }};
class ReportPrinter {public: void print(const std::string& data) { /* impresión */ }};En el buen ejemplo, cada clase es responsable de su propia tarea. Esto simplifica las pruebas y la modificación del código.
Principio Abierto-Cerrado (Open-Closed)
Teoría: Las entidades de software (clases, módulos, funciones) deben estar abiertas para la extensión, pero cerradas para la modificación.
Práctica en C++: Se logra mediante la abstracción y el polimorfismo.
// ❌ Mal: para agregar una nueva figura, hay que cambiar el códigoclass AreaCalculator {public: double calculateArea(std::string shapeType, double param) { if (shapeType == "circle") { return 3.14 * param * param; } else if (shapeType == "square") { return param * param; } return 0.0; }};
// ✅ Bien: usamos polimorfismoclass Shape {public: virtual double area() const = 0; virtual ~Shape() = default;};
class Circle : public Shape { double radius;public: Circle(double r) : radius(r) {} double area() const override { return 3.14159 * radius * radius; }};
class Square : public Shape { double side;public: Square(double s) : side(s) {} double area() const override { return side * side; }};
class AreaCalculatorOCP {public: double calculate(const Shape& shape) { return shape.area(); }};Ahora, para agregar una nueva figura (por ejemplo, un triángulo), no es necesario cambiar A