SOLID is an acronym for five design principles that make software designs more understandable, flexible, and maintainable.
Single Responsibility Principle (SRP)
A class should have only one reason to change, meaning it should have only one job or responsibility.
Bad Example (C++ Code) :
class User {
private:
string name;
string email;
public:
void saveToDatabase() {
// Database logic here
}
void sendEmail() {
// Email sending logic here
}
void generateReport() {
// Report generation logic here
}
};Good Example:
#include<bits/stdc++.h>
using namespace std;
class User{
private:
int id;
string name;
string emailAddress;
public:
User(int id, string name, string emailAddress){
this\->id = id;
this\->name = name;
this\->emailAddress = emailAddress;
}
int getId() const {
return id;
}
string getName() const {
return name;
}
string getEmailId() const {
return emailAddress;
}
};
class UserRepository{
public:
void saveToDB( User & u1){
int userId= u1.getId();
string userName = u1.getName();
string userEmail = u1.getEmailId();
cout<<"User Data "<< userId <<" , "<< userName<<", "<< userEmail <<"Saved In Db "<<endl;
}
};
class EmailService{
public:
void emailSend( const User & u1){
int userId= u1.getId();
string userName = u1.getName();
string userEmail = u1.getEmailId();
cout<<"Email send to " << userName << "at his Email Address"<< userEmail<<endl;
}
};
class UserStrategy{
private:
UserRepository ur;
EmailService um;
public:
// User u1 (123, "alpha", "alpha@gmail.com");
void userAllServices( User &u1){
ur.saveToDB(u1);
um.emailSend(u1);
}
void userDataSaveService( User &u1){
ur.saveToDB(u1);
}
void userEmailSendService( User & u1){
um.emailSend(u1);
}
};
int main(){
User u (1, "alpha", "alpha@gmail.com");
UserStrategy us;
us.userAllServices(u);
us.userEmailSendService(u);
us.userDataSaveService(u);
};Key Benefit: Each class has a single, well-defined responsibility, making the code easier to maintain and test.
Open/Closed Principle (OCP)
Definition: Software entities should be open for extension but closed for modification.
Bad Example:
public:
double width, height;
};
class Circle {
public:
double radius;
};
class AreaCalculator {
public:
double calculateArea(void\* shape, string type) {
if (type == "rectangle") {
Rectangle\* rect = static\_cast<Rectangle\*>(shape);
return rect->width \* rect->height;
} else if (type == "circle") {
Circle\* circle = static\_cast<Circle\*>(shape);
return 3.14 \* circle->radius \* circle->radius;
}
return 0;
}
};Good Example:
// OCP -> class must be extensible not modified
using namespace std;
class paymentMethod{
public:
virtual void pay() = 0;
};
class CardPayment : public paymentMethod{
private:
int cardNumber;
public:
CardPayment(int cardNumber){
this\->cardNumber = cardNumber;
}
void pay() override {
cout<<"Payment Done using card Number "<< cardNumber<<endl;
}
};
class UpiPayment : public paymentMethod{
private:
string upiId;
public:
UpiPayment(string upiId){
this\->upiId = upiId;
}
void pay() override {
cout<<"Payment Done using UPI ID "<< upiId<<endl;
}
};
class paymentProcessor{ // this is an orechestration layer/ Business we can add validation, retries, logging.
public:
void userPayment( paymentMethod \* m ){
// we can add validation
m->pay();
// loggings
// retries
}
};
int main(){
paymentMethod \*p1 = new CardPayment(123456);
paymentMethod \*p2 = new UpiPayment("abhi@334sbi");
paymentProcessor p;
p.userPayment(p1);
p.userPayment(p2);
p1->pay();
}Key Benefit: New shapes can be added without modifying existing code, reducing the risk of breaking existing functionality.
Liskov Substitution Principle (LSP)
Definition: Objects of a superclass should be replaceable with objects of its subclasses without breaking the application.
Bad Example:
class Bird {
public:
virtual void fly() {
cout << "Flying..." << endl;
}
};
class Penguin : public Bird {
public:
void fly() override {
throw runtime\_error("Penguins can't fly!");
}
};Good Example:
class Bird {
public:
virtual void move() = 0;
virtual ~Bird() = default;
};
class FlyingBird : public Bird {
public:
void move() override {
fly();
}
virtual void fly() {
cout << "Flying..." << endl;
}
};
class Penguin : public Bird {
public:
void move() override {
swim();
}
void swim() {
cout << "Swimming..." << endl;
}
};
class Sparrow : public FlyingBird {
public:
void fly() override {
cout << "Sparrow flying..." << endl;
}
};Key Benefit: Substituting a derived class for a base class doesn't cause unexpected behavior.
Interface Segregation Principle (ISP)
Definition: No client should be forced to depend on methods it does not use. Split large interfaces into smaller, more specific ones.
Bad Example:
class Worker {
public:
virtual void work() = 0;
virtual void eat() = 0;
virtual void sleep() = 0;
virtual ~Worker() = default;
};
class Robot : public Worker {
public:
void work() override {
cout << "Robot working..." << endl;
}
void eat() override {
// Robots don't eat - forced to implement
}
void sleep() override {
// Robots don't sleep - forced to implement
}
};Good Example:
class Workable {
public:
virtual void work() = 0;
virtual ~Workable() = default;
};
class Eatable {
public:
virtual void eat() = 0;
virtual ~Eatable() = default;
};
class Sleepable {
public:
virtual void sleep() = 0;
virtual ~Sleepable() = default;
};
class Human : public Workable, public Eatable, public Sleepable {
public:
void work() override {
cout << "Human working..." << endl;
}
void eat() override {
cout << "Human eating..." << endl;
}
void sleep() override {
cout << "Human sleeping..." << endl;
}
};
class Robot : public Workable {
public:
void work() override {
cout << "Robot working..." << endl;
}
};Key Benefit: Classes only implement interfaces they actually need, avoiding unnecessary dependencies.
Dependency Inversion Principle (DIP)
Definition: High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend on details; details should depend on abstractions.
Bad Example:
class MySQLDatabase {
public:
void connect() {
cout << "Connecting to MySQL..." << endl;
}
};
class UserService {
private:
MySQLDatabase database; // Tight coupling
public:
void getUser() {
database.connect();
// Get user logic
}
};Good Example:
class Database {
public:
virtual void connect() = 0;
virtual ~Database() = default;
};
class MySQLDatabase : public Database {
public:
void connect() override {
cout << "Connecting to MySQL..." << endl;
}
};
class PostgreSQLDatabase : public Database {
public:
void connect() override {
cout << "Connecting to PostgreSQL..." << endl;
}
};
class UserService {
private:
Database\* database; // Depends on abstraction
public:
UserService(Database\* db) : database(db) {}
void getUser() {
database->connect();
// Get user logic
}
};ANOTHER EXAMPLE
using namespace std;
class PaymentMethod{
public:
virtual void pay() = 0;
virtual void validate() = 0;
// virtual ~PaymentMethod() {} ;
};
class CardPayment : public PaymentMethod{
private:
int cardNumber;
public:
CardPayment(int cardNumber){
this\->cardNumber = cardNumber;
}
void validate(){
cout<<"validating card payment "<<endl;
}
void pay() override {
cout<<"Payment is Done using card -> "<<cardNumber<<endl;
}
};
class upiPayment : public PaymentMethod{
private:
string upiId;
public:
upiPayment(string upiId){
this\->upiId = upiId;
}
void validate(){
cout<<"validating upi payment "<<endl;
}
void pay() override {
cout<<"Payment is Done using upiId -> "<<upiId<<endl;
}
};
class walletPyment : public PaymentMethod{
private:
int walletId;
public:
walletPyment(int walletId){
this\->walletId = walletId;
}
void validate(){
cout<<"validating wallet payment "<<endl;
}
void pay () override {
cout<<"Payment is Done using wallet -> "<<walletId<<endl;
}
};
class PaymentServices{
private :
PaymentMethod \* pm;
public:
PaymentServices(PaymentMethod \* pm){
this\->pm = pm;
}
// pm->pay();
void paymentService(){
pm->validate();
pm -> pay();
}
};
int main(){
PaymentMethod \* p1 = new CardPayment(1234);
PaymentMethod \* p2 = new upiPayment("omkar@sbi123");
PaymentMethod \* p3 = new walletPyment(98765);
PaymentServices \* p = new PaymentServices(p1);
PaymentServices \* p4 = new PaymentServices(p2);
PaymentServices \* p5 = new PaymentServices(p3);
p->paymentService();
p4->paymentService();
p5->paymentService();
}Thanks for Reading... If you like this post hit like and share among friends.



