Princípios SOLID em C++: teoria e prática do A ao Z
SOLID é um acrônimo que designa cinco princípios fundamentais de programação e design orientados a objetos, formulados por Robert Martin (também conhecido como "Tio Bob"). Para C++, esses princípios são especialmente importantes, pois permitem criar código flexível, sustentável e testável, que é fácil de escalar. Neste artigo, analisaremos cada princípio do ponto de vista teórico e o apoiaremos com exemplos práticos em C++.
O que é SOLID e por que é necessário?
SOLID é um conjunto de regras que ajuda o desenvolvedor a evitar código "ruim". A observância desses princípios leva à redução do acoplamento (coupling) e ao aumento da coesão (cohesion) dos módulos. Em C++, onde os poderosos recursos de templates e herança múltipla podem facilmente levar ao caos, SOLID serve como uma bússola confiável. Cada letra do acrônimo é decifrada da seguinte forma:
- S — Single Responsibility Principle (Princípio da Responsabilidade Única)
- O — Open-Closed Principle (Princípio do Aberto/Fechado)
- L — Liskov Substitution Principle (Princípio da Substituição de Liskov)
- I — Interface Segregation Principle (Princípio da Segregação de Interface)
- D — Dependency Inversion Principle (Princípio da Inversão de Dependência)
Princípio da Responsabilidade Única (Single Responsibility)
Teoria: Uma classe deve ter apenas uma razão para mudar. Em outras palavras, a classe deve ser responsável por apenas uma parte da funcionalidade do programa.
Prática em C++: A violação desse princípio geralmente leva a "objetos divinos" que fazem tudo de uma vez.
// ❌ Ruim: a classe viola o SRPclass Report {public: void generateReport() { /* geração de dados */ } void saveToFile(const std::string& filename) { /* gravação em arquivo */ } void printReport() { /* envio para impressora */ }};
// ✅ Bom: separamos as 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) { /* impressão */ }};No bom exemplo, cada classe é responsável por sua tarefa. Isso simplifica o teste e a modificação do código.
Princípio do Aberto/Fechado (Open-Closed)
Teoria: Entidades de software (classes, módulos, funções) devem estar abertas para extensão, mas fechadas para modificação.
Prática em C++: É alcançado por meio de abstração e polimorfismo.
// ❌ Ruim: para adicionar uma nova forma, é necessário alterar o 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; }};
// ✅ Bom: 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(); }};Agora, para adicionar uma nova forma (por exemplo, um triângulo), não é necessário alterar A