Introduction: Why SOLID is Important for a C++ Developer?
\n\nSOLID is an acronym for five fundamental principles of object-oriented programming, formulated by Robert Martin (Uncle Bob). For a C++ developer, these principles are not just abstract theory, but a practical tool that helps create flexible, maintainable, and testable applications. In the world of C++, where memory management and performance are critical, following SOLID helps avoid "spaghetti code" and simplifies refactoring.
\n\nIn this article, we will break down each SOLID principle with concrete examples in C++. You will see how violating the principles leads to problems and how to fix them. We will use modern C++ (C++11/14/17/20) to keep the examples relevant.
\n\n1. Single Responsibility Principle (SRP)
\n\nEssence of the Principle
\nA class should have only one reason to change. In other words, each class is responsible for only one functionality or aspect of the system.
\n\nSRP Violation in C++
\nConsider a class that violates SRP: it simultaneously manages report data and its formatting.
\n\nclass Report {\npublic:\n void generateReport(const std::vector<Data>& data) {\n // Data processing logic\n m_data = data;\n }\n \n void saveToFile(const std::string& filename) {\n // File saving logic\n std::ofstream file(filename);\n for (const auto& item : m_data) {\n file << item.toString() << "\\n";\n }\n }\n \n void printToConsole() {\n // Console output logic\n for (const auto& item : m_data) {\n std::cout << item.toString() << std::endl;\n }\n }\n \nprivate:\n std::vector<Data> m_data;\n};\n\nProblem: if the output format changes, you have to change the Report class. If the data storage method changes — also. This violates SRP.
\n\nFixing It: Separating Responsibilities
\n\n// Responsible only for report data\nclass ReportData {\npublic:\n void addData(const Data& item) {\n m_data.push_back(item);\n }\n \n const std::vector<Data>& getData() const {\n return m_data;\n }\n \nprivate:\n std::vector<Data> m_data;\n};\n\n// Responsible only for saving\nclass FileSaver {\npublic:\n void save(const std::string& filename, const ReportData& report) {\n std::ofstream file(filename);\n for (const auto& item : report.getData()) {\n file << item.toString() << "\\n";\n }\n }\n};\n\n// Responsible only for console output\nclass ConsolePrinter {\npublic:\n void print(const ReportData& report) {\n for (const auto& item : report.getData()) {\n std::cout << item.toString() << std::endl;\n }\n }\n};\n\nNow each class has one reason to change, making the code more flexible and easier to test.
\n\n2. Open-Closed Principle (OCP)
\n\nEssence of the Principle
\nSoftware entities (classes, modules, functions) should be open for extension but closed for modification. This means you should be able to add new functionality without changing existing code.
\n\nOCP Violation in C++
\nImagine a discount calculation system that uses if-else for each customer type.
\n\nclass DiscountCalculator {\npublic:\n double calculateDiscount(const std::string& customerType, double amount) {\n if (customerType == "regular") {\n return amount * 0.1; // 10%\n } else if (customerType == "vip") {\n return amount * 0.2; // 20%\n } else if (customerType == "premium") {\n return amount * 0.3; // 30%\n }\n