You are here

SOLID Principles-The Open Closed Principle

SOLID Principles-The Open Closed PrincipleAlmost everywhere when we talk about delivery of a product, the first step comes in mind is it’s design. The more focus we put into the design, the better the product will look. Every design has some design principles that need to be followed while designing a product.  Hence, design principles have a crucial role in any product delivery. Design Principles help teams with decision making. In this article, We will discuss about ‘SOLID Principles-The Open Closed Principle’.

A few simple principles or valuable questions can guide our team towards taking relevant decisions. SOLID Principles are the set of five principles used to design a software. In fact, the word ‘SOLID’ is the acronym for the set of five principles that contains the first letter of each principle. Let’s discuss SOLID Principles-The Open Closed Principle in detail and related concepts.

What is SOLID Principles-The Open Closed Principle?

The principle says, “Software components should be open for extension, but closed for modification”. Simply put, Software components like classes, modules, and functions should be open for extension but closed for modifications.

If we consider our software component as a class, then Classes should be open for extension, but closed for modification. In doing so, we stop ourselves from modifying existing code. Therefore, we also stop causing potential new bugs in an otherwise happy application.

Of course, the one exception to the rule is when fixing bugs in existing code. So, we should modify our class only at the time of bug fixing.

“Open to extension” means that you should design your classes so that new functionality can be added as new requirements are generated. “Closed for modification” means that once you developed a class you should never modify it, except to correct bugs.

These two parts of the principle appear to be contradictory. However, if you correctly structure your classes and their dependencies, you can add functionality without editing existing source code.

Design and code should be done in a way that new functionality should be added with minimum or no changes in the existing code. When needs to extend functionality – avoid tight coupling, don’t use if-else/switch-case logic, do code refactoring as required.

What are the ways of extending a class ?

The ways of extending a class include:

  • Inheriting from the class
  • Overwriting the required behaviors from the class
  • Extending certain behaviors of the class

Generally, you achieve this by referring to abstractions for dependencies, such as interfaces or abstract classes, rather than using concrete classes. We can add the functionality by creating new classes that implement the interfaces. This reduces the risk of introducing new bugs to existing code, leading to more robust software.

Example: Code that violates OCP

Let’s assume that we have to write a program that calculates area of various shapes. We start with creating a class for our first shape, let’s say Rectangle which has 2 attributes length & width.

public class Rectangle  {

     public Double length;
     public Double width;
}

Further, we create a class to calculate the area of this Rectangle which has a method calculateRectangleArea() which takes the Rectangle as an input parameter and calculates its area.

public class AreaCalculator {

     public Double calculateRectangleArea(Rectangle rectangle) {        

           return rectangle.length * rectangle.width;
     }
}

So far so good. Now let’s assume we have to write a program for our second shape circle. So, we promptly create a new class Circle with a single attribute radius.

public class Circle {

     public Double radius;
}

Then we modify AreaCalculator class to add circle calculations through a new method calculateCircleArea().

public class AreaCalculator{

     public Double calculateRectangleArea(Rectangle rectangle){       

           return rectangle.length * rectangle.width;
     }

     public Double calculateCircleArea(Circle circle){
          
           return (22 / 7) * circle.radius * circle.radius;

     }
}

However, please note that there are flaws in the way of designing our solution above.

Let’s say we have a new shape pentagon. In that case, we will again end up modifying the AreaCalculator class. As the types of shapes grows this becomes messier as AreaCalculator keeps on changing and any consumers of this class will have to keep on updating their libraries which contain AreaCalculator. As a result, AreaCalculator class will not be baselined(finalized) with surety as every time a new shape comes, it will be modified. So, this design is not closed for modification.

AreaCalculator will need to keep on adding their computation logic in newer methods. We are not really expanding the scope of shapes; rather we are simply doing piece-meal(bit-by-bit) solution for every shape that is added.

Example: Code that follows OCP

Let’s now see a more elegant design which solves the flaws in the above design by adhering to the Open/Closed Principle. First of all, we will make the design extensible. For this we need to first define a base type Shape and have Circle & Rectangle implement Shape interface. For example, below code demonstrates the concept.

public interface Shape {

     public Double calculateArea();

}

public class Rectangle implements Shape {

     Double length;
     Double width;

     public double calculateArea() {

           return length * width;
     }
}

public class Circle implements Shape {

     public Double radius;

     public Double calculateArea() {

           return (22 / 7) * radius * radius;
     }
}


As aforementioned, there is a base interface Shape. All shapes now implement the base interface Shape. Shape interface has an abstract method calculateArea(). Both circle & rectangle provide their own overridden implementation of calculateArea() method using their own attributes. If in future we want to calculate area of other shapes like triangle, square etc., we can implement the Shape interface without changing any class.

We have brought in a degree of extensibility as shapes are now an instance of Shape interfaces. This allows us to use Shape instead of individual classes.
The last point is the consumer of these shapes. The consumer will be the AreaCalculator class which would now look like this.

public class AreaCalculator {

     public Double calculateShapeArea(Shape shape) {

           return shape.calculateArea();
     }
}

This AreaCalculator class now fully removes our design flaws noted above and provides a clean solution which adheres to the Open-Closed Principle.

What is the benefits of Open Closed Principle?

Below are some of the benefits of applying open closed principle in your code :

Easier Extensibility

Needless to say, Open-closed Principle offers a better extensibility to your code. When you write your code in such a way that it has no modification and allows extension, you will get better extensibility on it’s own.

Easier to Maintain

The Principle suggests using interfaces. Interfaces in code offers an additional level of abstraction which in turn enables loose coupling. The implementations of an interface are independent of each other and don’t need to share any code. Hence, you can easily maintain your code with client’s keep changing requirements.

Flexibility

When you follow the open closed principle in writing your code, you will have better flexibility to extend it. Further, if any change request arises in future, your code will be more flexible to extend.

What is the limitations of Open Closed Principle?

Whenever we incorporate any change to the code as an extension, we need to do unit testing before and after implementing the same.

What are the Design Patterns that follow Open Closed Principle?

There are multiple of design patterns that help us to extend code without changing it. For example, the Decorator pattern offers us to follow the Open Close principle. Furthermore, we may use the Factory Method, Strategy pattern and the Observer pattern to design an application with minimum changes in the existing code.

FAQ

How does OCP help in agile software development practices? 

OCP helps in agile practices by promoting code that is more adaptable to frequently changing requirements. It facilitates incremental development and makes it easier to accommodate new user stories or features during sprints.

Does OCP apply only to classes, or does it also relate to methods and modules?

OCP applies to all software entities, including classes, methods, and modules. Any unit of code that can be extended without modification while maintaining existing functionality follows OCP.

How does OCP relate to the use of design patterns in software development?

Many design patterns, such as the Strategy Pattern and Decorator Pattern, adapt OCP by allowing us to extend functionality without modifying existing classes. Design patterns generally provide practical ways to implement OCP.

What is the main goal of the Open-Closed Principle (OCP)?

The primary goal of OCP is to encourage code extensibility and maintainability. It allows us to add new features or behaviors to a system without having to modify existing, working code, reducing the risk of introducing bugs.

Does applying OCP mean that no code modification is allowed once a class is defined?

No, OCP does not mean that code should never be modified. Alternatively, it suggests that modifications should be limited to fixing bugs or making essential improvements, not for adding new features or behaviors.

 

That’s all about ‘SOLID Principles-The Open Closed Principle’. Moreover, Wikipedia defines this Principle like this. In order to learn the most commonly used design principles, kindly visit article on ‘OOPs Design Principles‘. Apart from ‘SOLID Principles-The Open Closed Principle’, other principles are discussed as separate articles respectively.

Links to Other Design Principles

Single Responsibility Principle

Liskov’s Substitution Principle(LSP)

Interface Segregation Principle(ISP)

Dependency Inversion Principle(DIP)

Leave a Reply


Top