SOLID Principle: OCP Implementation in C++ (Deep Dive)

Ankur Mani Tripathi
3 min readMar 30, 2024

The Open/Closed Principle (OCP) in C++ states that software entities (classes, modules, functions, etc.) should be open for extension but closed for modification. This means that you should be able to extend the behavior of a system without modifying its existing code.

For SRP (Single Responsibility Principle), check out the blog —
https://medium.com/@gmtripathi70/solid-principle-srp-implementation-in-c-deep-dive-62b8b058fcbe

Let’s make our SRP example OCP adhere,

#include<iostream>
#include<vector>
using namespace std;

class Item_abstract{
public:
virtual ~Item_abstract(){}
virtual string getId() const = 0;
virtual float getprice() const = 0;;
};

class Item : public Item_abstract{
private:
string name;
string Id;
float price;
public:
Item(string name, string Id, float price){
this->name = name;
this->Id = Id;
this->price = price;
}
string getId() const override {return Id;}
float getprice() const override {return price;}

};


class CustomerPersonalDetails_abstract{
public:
virtual ~CustomerPersonalDetails_abstract(){}
virtual void setName(string name, int Id) = 0;
virtual string getName() const = 0;
virtual int getId() const = 0;
};

class CustomerPersonalDetails : public CustomerPersonalDetails_abstract{
private:
string name;
int Id;
public:
void setName(string name, int Id) override {
this->name = name; this->Id = Id;
}
string getName()const override { return name; }
int getId() const override { return Id; }
};

class ItemDetails_Abstract{
public:
virtual ~ItemDetails_Abstract(){}
virtual void addItem(const Item& item) = 0;
virtual void removeItem(const string& itemId) = 0;
virtual float calculateTotalSum() const = 0;
virtual vector<Item> getItems() const = 0;
};

class ItemDetails : public ItemDetails_Abstract{
private:
vector<Item> items;
float totalAmount;
public:
void addItem(const Item& item) override {items.push_back(item);}
void removeItem(const string& itemId) override {
for (auto it = items.begin(); it != items.end(); ++it) {
if (it->getId() == itemId) {
items.erase(it);
break;
}
}
};
float calculateTotalSum() const override {
float total = 0;
for(auto it : items){
total += it.getprice();
}
return total;
}
vector<Item> getItems() const override {
return items;
}
};

class GenerateInvoice_Abstract{
public:
virtual ~GenerateInvoice_Abstract(){}
virtual void printInvoice(const vector<Item>& items) const = 0;
};

class GenerateInvoice : public GenerateInvoice_Abstract{
public:
void printInvoice(const vector<Item>& items) const override {
float total = 0;
for(auto it : items){
total += it.getprice();
}
cout << "Your bill is : " << total << endl;
}
};

int main() {
// Example usage
CustomerPersonalDetails customer;
customer.setName("John Doe", 123);

ItemDetails itemDetails;
itemDetails.addItem(Item("Item1", "1", 10.5));
itemDetails.addItem(Item("Item2", "2", 20.75));

GenerateInvoice invoiceGenerator;
invoiceGenerator.printInvoice(itemDetails.getItems());

return 0;
}
Output --
Your bill is : 31.25

The code above demonstrates adherence to the Open/Closed Principle (OCP). Let’s analyze how it adheres to OCP:

  1. Abstraction through Interfaces (Abstract Base Classes):
  • The classes Item_abstract, CustomerPersonalDetails_abstract, ItemDetails_Abstract, and GenerateInvoice_Abstract serve as abstract base classes defining interfaces for their concrete implementations.
  • These interfaces allow for extension by defining the contract that derived classes must adhere to without modifying the existing code.

2. Polymorphic Behavior:

  • Each concrete class (Item, CustomerPersonalDetails, ItemDetails, and GenerateInvoice) implements the methods defined in their respective abstract base classes.
  • This polymorphic behavior enables the client code to work with objects of these classes through their common interfaces.

3. Example Usage:

  • The main function demonstrates the usage of these classes through their interfaces without needing to modify the code of the client (main) function.
  • It creates instances of concrete classes (CustomerPersonalDetails, ItemDetails) and uses them interchangeably through their abstract interfaces (CustomerPersonalDetails_abstract, ItemDetails_Abstract).
  • Similarly, it uses the GenerateInvoice_Abstract interface to invoke the printInvoice method of the GenerateInvoice class.

4. Extensibility:

  • You can easily extend the functionality of the system by creating new classes that implement the existing abstract interfaces.
  • For example, you can create a new class that implements ItemDetails_Abstract to handle items in a different way, and the client code won't need to change.

In conclusion, the code above effectively adheres to the Open/Closed Principle by allowing for extension without modification, thanks to the use of abstract interfaces and polymorphism.

--

--

No responses yet