Open/Closed Principle (OCP)

Kashish Singh Yadav
3 min readApr 20, 2024

--

The open–closed principle (OCP) is a SOLID principle of object-oriented programming. It states that software entities should be open for extension, but closed for modification.

we can implement ocp by following these

  1. using inheritance/abstract class
  2. composition/injection
  3. parameter/arguments

Imagine you’re managing an online shopping platform, and you need to develop a system to calculate order totals. You want your system to be flexible so that it can handle different types of orders without constantly modifying existing code. This is where the Open/Closed Principle comes into play.

First, you create an abstract base class called Order. This class serves as a blueprint for all types of orders in your system. It contains an abstract method calculate() which defines the behavior for calculating the total cost of an order. By making Order abstract, you ensure that all concrete order types must implement their own calculate() method

Now, let’s look at two concrete implementations of the Order class: StandardOrder and DiscountOrder.

The StandardOrder class represents a basic order without any discounts applied. It takes a list of items as input and calculates the total cost by summing up the prices of all items. This class follows the Open/Closed Principle because it extends the behavior of the Order class without modifying it. If you need to add new features or change the way standard orders are calculated, you can do so by creating a new class without touching the existing StandardOrder code.

Next, the DiscountOrder class represents an order with a discount applied. It takes a discount rate and a list of items as input. Similar to StandardOrder, it calculates the total cost by summing up the prices of all items but applies a discount based on the provided rate. Like StandardOrder, DiscountOrder follows the Open/Closed Principle by extending the behavior of the Order class without modifying it.

By designing your system this way, you ensure that it is open for extension but closed for modification. If you need to introduce new types of orders in the future, you can simply create new classes that inherit from the Order class and implement their own calculate() method. This modular approach makes your codebase more flexible and easier to maintain as your application grows and evolves.

from abc import ABC, abstractmethod 

class Item:
def __init__(self, name, price):
self.name = name
self.price = price

class Order(ABC):
@abstractmethod
def calculate(self):
pass

class StandardOrder(Order):
def __init__(self, items):
self.items = items

def calculate(self):
total = [item.price for item in self.items]
return sum(total)

class DiscountOrder(Order):
def __init__(self, disrate, items):
self.items = items
self.disrate = disrate

def calculate(self):
total = [item.price for item in self.items]
return sum(total) * (1 - self.disrate)

standard_items = [Item('skrit', 10), Item('cargo', 20), Item('tee', 60)]
obj1 = StandardOrder(standard_items)
print('The standard order total is:', obj1.calculate())

discount_items = [Item('contour', 60), Item('primer', 100), Item('tint', 40)]
obj2 = DiscountOrder(0.2, discount_items)
print('The total amount after discount is:', obj2.calculate())

Example 2

In this code example, we’ve demonstrated the Open/Closed Principle (OCP) using Python’s abstract classes and polymorphism. The OCP suggests that software entities should be open for extension but closed for modification.

We start by defining an abstract base class Shape, which contains an abstract method area(). This class serves as a blueprint for different geometric shapes.

Next, we create concrete subclasses (Square, Rectangle, and Circle) that inherit from Shape. Each subclass implements its own version of the area() method, tailored to calculate the area specific to its shape.

By adhering to the OCP, our code remains closed for modification, as we don’t need to alter existing classes when introducing new shapes. Instead, we extend functionality by adding new subclasses.

When we instantiate objects of different shapes and call their area() methods, Python's polymorphism ensures that the correct implementation of area() is invoked for each shape.

This design promotes code reuse, maintainability, and flexibility, making it easier to manage and extend our codebase as requirements evolve.

from abc import ABC,abstractmethod 
class Shape(ABC):# python not support interface so we used abstractclass+ multiple inheritance
@abstractmethod
def area(self):
pass

class Square(Shape):
def __init__(self,side):
self.side=side
def area(self):
return self.side**2

class Rectangle(Shape):
def __init__(self,length,breath):
self.length=length
self.breath=breath

def area(self):
return self.length*self.breath

class Circle(Shape):
def __init__(self,radius):
self.radius=radius
def area(self):
return 3.14*self.radius*self.radius

obj1=Square(10)
print("the area of square is",obj1.area())
obj2=Rectangle(5,3)
print("The area of Rectange is ",obj2.area())
obj3=Circle(6)
print("The area of circle is ",obj3.area())

--

--