Princípios SOLID em C++: da teoria à prática
Os princípios SOLID são cinco regras fundamentais de design orientado a objetos, formuladas por Robert Martin. Eles ajudam a criar código flexível, escalável e de fácil manutenção. Neste artigo, vamos analisar cada princípio com exemplos em C++, desde a teoria simples até casos práticos reais.
1. Single Responsibility Principle (SRP) — Princípio da Responsabilidade Única
Teoria: Cada classe deve ter apenas um motivo para ser alterada. Em outras palavras, a classe é responsável por apenas uma parte da funcionalidade do programa.
Problema: Frequentemente, os desenvolvedores colocam em uma única classe a lógica de manipulação de dados, seu salvamento e exibição na tela. Isso viola o SRP.
Prática em C++: Vamos dividir a classe Report em três classes separadas.
// Ruim: viola o SRPclass Report { std::string title; std::string content;public: void generate() { /* geração do relatório */ } void saveToFile(const std::string& filename) { /* salvamento em arquivo */ } void print() { /* impressão */ }};
// Bom: respeita o SRPclass Report { std::string title; std::string content;public: void generate() { /* apenas geração */ }};
class ReportSaver {public: void saveToFile(const Report& report, const std::string& filename) { /* salvamento */ }};
class ReportPrinter {public: void print(const Report& report) { /* impressão */ }};Conclusão: Agora cada classe é responsável pela sua própria tarefa. O código se tornou mais fácil de testar e modificar.
2. Open-Closed Principle (OCP) — Princípio Aberto/Fechado
Teoria: Entidades de software (classes, módulos, funções) devem estar abertas para extensão, mas fechadas para modificação. Ou seja, devemos ser capazes de adicionar novas funcionalidades sem alterar o código existente.
Prática em C++: Usamos polimorfismo e classes abstratas.
#include <iostream>#include <vector>#include <memory>
// Classe base para formasclass 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; }};
// Classe para calcular a área total — não requer alterações ao adicionar novas formasclass 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;}Conclusão: Podemos adicionar novas formas (por exemplo, Triangle) sem modificar o AreaCalculator.
3. Liskov Substitution Principle (LSP) — Princípio da Substituição de Barbara Liskov
Teoria: Os objetos em um programa devem ser substituíveis por instâncias de seus subtipos sem quebrar a correção do programa. Simplificando, se você tem uma classe pai e uma classe filha, você deve ser capaz de usar a filha onde a pai é esperada, e tudo deve funcionar.
Prática