Solid_principles
SOLID Principles in C++
Section titled “SOLID Principles in C++”SOLID is a set of five design principles that make software more understandable, flexible, and maintainable.
Single Responsibility Principle (SRP)
Section titled “Single Responsibility Principle (SRP)”A class should have only one reason to change.
// Bad: Multiple responsibilitiesclass User {public: void setName(const std::string& name) { name_ = name; } void saveToDatabase() { /* Save to DB */ } void sendEmail() { /* Send email */ }};
// Good: Separate responsibilitiesclass User {private: std::string name;public: void setName(const std::string& n) { name = n; } std::string getName() const { return name; }};
class UserRepository { void save(const User& user) { /* Save to DB */ }};
class EmailService { void send(const User& user, const std::string& msg) { /* Send */ }};Open/Closed Principle (OCP)
Section titled “Open/Closed Principle (OCP)”Software entities should be open for extension but closed for modification.
// Bad: Need to modify for new shapesclass AreaCalculator {public: double area(const std::string& shape, const std::vector<double>& params) { if (shape == "circle") { return 3.14 * params[0] * params[0]; } else if (shape == "rectangle") { return params[0] * params[1]; } // Need to add new case for each shape! return 0; }};
// Good: Open for extension, closed for modificationclass Shape {public: virtual double area() const = 0; virtual ~Shape() = default;};
class Circle : public Shape {private: double radius;public: Circle(double r) : radius(r) {} double area() const override { return 3.14 * radius * radius; }};
class Rectangle : public Shape {private: double width, height;public: Rectangle(double w, double h) : width(w), height(h) {} double area() const override { return width * height; }};
double totalArea(const std::vector<Shape*>& shapes) { double total = 0; for (const auto* s : shapes) { total += s->area(); // Works for any shape! } return total;}Liskov Substitution Principle (LSP)
Section titled “Liskov Substitution Principle (LSP)”Objects of a superclass should be replaceable with objects of its subclasses.
// Bad: Square inherits from Rectangle but changes behaviorclass Rectangle {protected: double width, height;public: void setWidth(double w) { width = w; } void setHeight(double h) { height = h; }};
class Square : public Rectangle {public: void setWidth(double w) { width = height = w; // Breaks expectations! } void setHeight(double h) { width = height = h; // Breaks expectations! }};
void process(Rectangle& r) { r.setWidth(5); r.setHeight(10); // Expected: width=5, height=10 // With Square: width=10, height=10 - BROKEN!}Interface Segregation Principle (ISP)
Section titled “Interface Segregation Principle (ISP)”Prefer small, specific interfaces over large, general ones.
// Bad: Fat interfaceclass Worker {public: virtual void work() = 0; virtual void eat() = 0; virtual void sleep() = 0;};
class Robot : public Worker {public: void work() override { /* Work */ } void eat() override { /* Robot doesn't eat! */ } void sleep() override { /* Robot doesn't sleep! */ }};
// Good: Segregated interfacesclass Workable {public: virtual void work() = 0;};
class Eatable {public: virtual void eat() = 0;};
class Sleepable {public: virtual void sleep() = 0;};
class Human : public Workable, public Eatable, public Sleepable {public: void work() override { /* Work */ } void eat() override { /* Eat */ } void sleep() override { /* Sleep */ }};
class Robot : public Workable {public: void work() override { /* Work */ }};Dependency Inversion Principle (DIP)
Section titled “Dependency Inversion Principle (DIP)”Depend on abstractions, not concrete implementations.
// Bad: Depends on concrete classclass MySQLDatabase {public: void connect() { /* Connect to MySQL */ }};
class UserService {private: MySQLDatabase db; // Tightly coupledpublic: void save() { db.connect(); }};
// Good: Depend on abstractionclass Database {public: virtual void connect() = 0; virtual ~Database() = default;};
class MySQLDatabase : public Database {public: void connect() override { /* Connect to MySQL */ }};
class PostgreSQLDatabase : public Database {public: void connect() override { /* Connect to PostgreSQL */ }};
class UserService {private: Database& db; // Depends on abstractionpublic: UserService(Database& database) : db(database) {} void save() { db.connect(); }};Key Takeaways
Section titled “Key Takeaways”- S: One class, one responsibility
- O: Open for extension, closed for modification
- L: Subclasses should be substitutable for base classes
- I: Many small interfaces > one large interface
- D: Depend on abstractions, not concretions
Next Steps
Section titled “Next Steps”Now let’s learn about build systems - essential for any C++ project.
Next Chapter: 10_build_systems/49_cmake.md - CMake Mastery