tags:
- Cpp
SOLID Design Principles (NC)
This note is all about the SOLID principals, which define five fundamental design principles for writing solid code. Those principles are:
The SOLID principles rooted in object-oriented programming, it should be significantly improving OOP-based code structure and maintainability, making software easier to extend and modify with a good understanding of these principles. Who knows, everything's trade-offs today.
"A class should have only one reason to change."
hard to know if you are get it right, easy to know if you are get it wrong
A class should have a single, well-defined purpose. If a class has multiple responsibilities, changes in one aspect may lead to unexpected modifications in another, increasing the risk of bugs.
❌ Violation: A class doing too many things
java
class ReportManager {
void generateReport() { /* Logic to create report */ }
void printReport() { /* Logic to print report */ }
void saveReport() { /* Logic to save report */ }
}
✅ SRP-Compliant Design
java
class ReportGenerator {
void generateReport() { /* Creates report */ }
}
class ReportPrinter {
void printReport() { /* Prints the report */ }
}
class ReportSaver {
void saveReport() { /* Saves report to disk */ }
}
✔️ Improved readability and maintainability ✔️ Easier testing and debugging ✔️ Prevents unintended coupling between features
"Software entities should be open for extension but closed for modification."
A class should allow new functionality to be added without modifying existing code. This reduces the risk of introducing new bugs when extending a system.
❌ Violation: Modifying existing code
python
class Rectangle:
def draw(self):
print("Drawing Rectangle")
class Circle:
def draw(self):
print("Drawing Circle")
def render(shape):
if isinstance(shape, Rectangle):
shape.draw()
elif isinstance(shape, Circle):
shape.draw()
✅ OCP-Compliant Design (Using Polymorphism)
python
class Shape:
def draw(self):
pass
class Rectangle(Shape):
def draw(self):
print("Drawing Rectangle")
class Circle(Shape):
def draw(self):
print("Drawing Circle")
def render(shape: Shape):
shape.draw()
✔️ Simplifies adding new functionality ✔️ Avoids modifying stable and tested code ✔️ Reduces maintenance cost
"Subtypes must be substitutable for their base types."
Objects of a derived class should be able to replace objects of the base class without causing errors or unexpected behavior.
❌ Violation: Breaking expected behavior
cpp
class Bird {
public:
virtual void fly() { cout << "Flying"; }
};
class Penguin : public Bird {
public:
void fly() override { throw std::logic_error("Penguins can't fly!"); }
};
✅ LSP-Compliant Design
cpp
class Bird {
public:
virtual void move() { cout << "Bird moves"; }
};
class Penguin : public Bird {
public:
void move() override { cout << "Penguin swims"; }
};
✔️ Prevents unexpected errors ✔️ Ensures reliable polymorphism ✔️ Improves code flexibility
"Clients should not be forced to depend on interfaces they do not use."
A class should only implement the methods it actually needs. Large, general-purpose interfaces should be broken down into smaller, more specific interfaces.
❌ Violation: A class forced to implement unnecessary methods
csharp
interface Worker {
void work();
void eat();
}
class Robot : Worker {
public void work() { Console.WriteLine("Working"); }
public void eat() { throw new NotImplementedException(); }
}
✅ ISP-Compliant Design
csharp
interface Workable {
void work();
}
interface Eatable {
void eat();
}
class Robot : Workable {
public void work() { Console.WriteLine("Working"); }
}
✔️ Avoids unnecessary dependencies ✔️ Improves flexibility and usability ✔️ Ensures smaller, more manageable interfaces
"High-level modules should not depend on low-level modules. Both should depend on abstractions."
Instead of a class directly depending on a concrete implementation, it should depend on an abstraction to make the system more flexible.
❌ Violation: Tight Coupling
java
class MySQLDatabase {
void connect() { /* MySQL connection logic */ }
}
class DataHandler {
private MySQLDatabase db = new MySQLDatabase();
void fetchData() { db.connect(); }
}
✅ DIP-Compliant Design (Using Interface)
java
interface Database {
void connect();
}
class MySQLDatabase implements Database {
public void connect() { /* MySQL connection logic */ }
}
class DataHandler {
private Database db;
public DataHandler(Database db) { this.db = db; }
void fetchData() { db.connect(); }
}
✔️ Loose coupling ✔️ Easier to swap dependencies ✔️ Facilitates unit testing
The SOLID principles provide a structured way to write clean, maintainable, and scalable code. By following these principles, developers can reduce technical debt, enhance software flexibility, and prevent common design pitfalls.
Would you like any further refinements or additional insights? 😊