In software development, the use of object-oriented design is crucial. It helps to write flexible, scalable, and reusable code. It is recommended that the developers follow SOLID principles when writing a code. One of the five SOLID principles is the open/closed principle. The principle states that software entities like class, modules, functions, etc.; should be able to extend a class behavior without modifying it. This principle separates the existing code from modified mode to provide better stability, maintainability and minimizes the changes in the code. In a nutshell, the developer must need to change only a specific part of the code (a class or a function) every time a requirement changes.
Using a statically typed language like Java, C#, etc. the open/closed principle is generally achieved by using inheritance and polymorphism. Let’s understand it with a few examples.
Implementation:
Program to calculate the volume in which let us consider the task of building an application that calculates the volumes of all the geometric objects.
- The Cuboid class stores dimensions of the cuboid
- Later on, the Application class calculates the total volume of the geometric objects–which are only cuboids currently.
- Run class helps to run the whole program.
Example 1:
Java
// Java Program to illustrate Open Closed Principle // Class 1 // Helper class // To store dimensions of a cuboid // length, breadth and height class Cuboid { // Member variables public double length; public double breadth; public double height; } // Class 2 // Helper class // To calculate the volume of geometric objects class Application { // It returns the total volume of the geometric objects public double get_total_volume(Cuboid[] geo_objects) { // Variable to store total volume double vol_sum = 0 ; // Iteratively calculating the volume of each object // and adding it to the total volume for (Cuboid geo_obj : geo_objects) { // Iteratively calculating the volume of each object // and adding it to the total volume vol_sum += geo_obj.length * geo_obj.breadth * geo_obj.height; } // returning the to total volume return vol_sum; } } // Class 3 // Main Class // To demonstrate working of all classes public class GFG { // Main driver method public static void main(String args[]) { // Initializing a cuboid one & declaring dimensions by // creating an object of Cuboid class in main() method Cuboid cb1 = new Cuboid(); // Custom entries cb1.length = 5 ; cb1.breadth = 10 ; cb1.height = 15 ; // Similarly, initializing a cuboid2 and declaring dimensions // by creating object of Cuboid class in the main() method Cuboid cb2 = new Cuboid(); // Custom entries cb2.length = 2 ; cb2.breadth = 4 ; cb2.height = 6 ; // Initializing a cuboid3 and declaring dimensions by // creating object of Cuboid class in the main() method Cuboid cb3 = new Cuboid(); // Custom entries cb3.length = 3 ; cb3.breadth = 12 ; cb3.height = 15 ; // Now, declaring andinitializing Array of cuboids Cuboid[] c_arr = new Cuboid[ 3 ]; c_arr[ 0 ] = cb1; c_arr[ 1 ] = cb2; c_arr[ 2 ] = cb3; // Initializing the Application class Application app = new Application(); // Getting the total volume // using get_total_volume double vol = app.get_total_volume(c_arr); // Print and Display the Total Volume System.out.println( "The total volume is " + vol); } } |
The total volume is 1338.0
Now, lets say the customer wants the application to calculate the volume of a sphere as well. In order to accommodate new type of geometric object, the application also needs to be changed.
Example 2:
Java
// Java Program to illustrate Open Closed Principle // class 1 // Helper class // To store dimensions of a cuboid // used to store length, breadth and height of a cuboid class Cuboid { // Member variables of this class public double length; public double breadth; public double height; } // Class 2 // Helper class // To store dimensions of a sphere class Sphere { // Storing radius of a sphere public double radius; } // Class 3 // Helper class // This class helps to calculate the volume of geometric // objects class Application { // Returning the total volume of the geometric objects public double get_total_volume(Cuboid[] c_geo_objects, Sphere[] s_geo_objects) { // Variable used to store total volume double vol_sum = 0 ; // Iteratively calculating the volume of each Cuboid // and adding it to the total volume // Iterating using for each loop to // calculate the volume of a cuboid for (Cuboid geo_obj : c_geo_objects) { vol_sum += geo_obj.length * geo_obj.breadth * geo_obj.height; } // Iterating using for each loop to // calculate the volume of a cuboid for (Sphere geo_obj : s_geo_objects) { // Iteratively calculating the volume of each // Sphere and adding it to the total volume vol_sum += ( 4 / 3 ) * Math.PI * geo_obj.radius * geo_obj.radius * geo_obj.radius; } // Returning the to total volume return vol_sum; } } // Class 4 // Main class // To demonstrate working of all classes public class GFG { // Main driver method public static void main(String args[]) { // Initializing a cuboid one as well as declaring // its dimensions. Cuboid cb1 = new Cuboid(); cb1.length = 5 ; cb1.breadth = 10 ; cb1.height = 15 ; // Initializing a cuboid two as well as declaring // its dimensions. Cuboid cb2 = new Cuboid(); cb2.length = 2 ; cb2.breadth = 4 ; cb2.height = 6 ; ////Initializing a cuboid three as well as declaring /// its dimensions. Cuboid cb3 = new Cuboid(); cb3.length = 3 ; cb3.breadth = 12 ; cb3.height = 15 ; // Initializing and declaring an array of cuboids Cuboid[] c_arr = new Cuboid[ 3 ]; c_arr[ 0 ] = cb1; c_arr[ 1 ] = cb2; c_arr[ 2 ] = cb3; // Initializing a sphere one as well as declaring // its dimension. Sphere sp1 = new Sphere(); sp1.radius = 5 ; // Initializing a sphere two as well as declaring // its dimension. Sphere sp2 = new Sphere(); sp2.radius = 2 ; // Initializing a sphere three as well as declaring // its dimension. Sphere sp3 = new Sphere(); sp3.radius = 3 ; // Initializing and declaring an array of spheres Sphere[] s_arr = new Sphere[ 3 ]; s_arr[ 0 ] = sp1; s_arr[ 1 ] = sp2; s_arr[ 2 ] = sp3; // Initializing Application class Application app = new Application(); // Getting the total volume // using get_total_volume double vol = app.get_total_volume(c_arr, s_arr); // Print and display the total volume System.out.println( "The total volume is " + vol); } } |
The total volume is 1840.6548245743668
Output Explanation:
As we can see the application class had to be changed to accommodate the sphere. Any changes in a code can lead to some unexpected errors in the future–so it is not advisable to change the well-tested code every time requirements change. Let’s try to apply the Open Close principle and see if we can add a sphere (a new type of object) without making any changes to the application class.
Solution:
- Create an abstract class that serves as a base class for all types of objects.
- All the geometric objects have a set of dimensions and a get_volume method (both of which are different for each type of object).
- For each type of object (a geometric object in this case) inherit the ‘Geo_object’ class, add the dimensions for that type of object and override the ‘get_volume‘ method.
- As it is apparent that by shifting the volume calculation from the ‘Application’ class to a different class, adding a new type of geometric object would not require changing the ‘Application’ class.
Example 3:
Java
// Java Program to illustrate Open Closed Principle // Importing all classes from java.math package // to compute mathematic calculations import java.math.*; // Class 1 // Helper Class // Abstract class--which needs to be extended abstract class Geo_objects { // Abstract function--which needs to overriden public abstract double get_volume(); } // Class 2 // Helper Class // Extending the Geo_objects to fit cuboid dimensions class Cuboid_2 extends Geo_objects { // used to store length, breadth and height of a cuboid public double length; public double breadth; public double height; // overrided function to calculate // the volume of a cuboid // @Override public double get_volume() { return length * breadth * height; } } // Class 3 // Helper Class // Extending Geo_objects to fit sphere dimension class Sphere_2 extends Geo_objects { // To store radius of a sphere public double radius; // Overrided function to calculate // the volume of a sphere //@Override public double get_volume() { return ( 4 / 3 ) * Math.PI * radius * radius * radius; } } // Class 4 // Helper class // To calculate the volume of geometric objects class Application { public double get_total_volume(Geo_objects[] geo_objects) { // Initially initializing sum to zero double vol_sum = 0 ; // Iterating using for each loop for (Geo_objects geo_obj : geo_objects) { vol_sum += geo_obj.get_volume(); } return vol_sum; } } // Class 5 // Main class // To demonstrate working of all classes public class GFG { // Main driver method public static void main(String args[]) { // Initializing cuboid1 as well as declaring // its dimensions. Cuboid_2 cb1 = new Cuboid_2(); // Custom entries cb1.length = 5 ; cb1.breadth = 10 ; cb1.height = 15 ; // Initializing Cuboid2 as well as declaring // its dimensions. Cuboid_2 cb2 = new Cuboid_2(); cb2.length = 2 ; cb2.breadth = 4 ; cb2.height = 6 ; // initializing Cuboid3 as well as declaring // its dimensions. Cuboid_2 cb3 = new Cuboid_2(); cb3.length = 3 ; cb3.breadth = 12 ; cb3.height = 15 ; // initializing Sphere1 as well as declaring // its dimension. Sphere_2 sp1 = new Sphere_2(); sp1.radius = 5 ; // initializing Sphere2 as well as declaring // its dimension. Sphere_2 sp2 = new Sphere_2(); sp2.radius = 2 ; // initializing Sphere3 as well as declaring // its dimension. Sphere_2 sp3 = new Sphere_2(); sp3.radius = 3 ; // Now, initializing and declaring // an array of Geo_objects Geo_objects[] g_arr = new Geo_objects[ 6 ]; // Setting Geo_objects to cuboid class g_arr[ 0 ] = cb1; g_arr[ 1 ] = cb2; g_arr[ 2 ] = cb3; // Setting Geo_objects to sphere class g_arr[ 3 ] = sp1; g_arr[ 4 ] = sp2; g_arr[ 5 ] = sp3; // Initializing the Application class Application app = new Application(); // Getting the total volume // using get_total_volume double vol = app.get_total_volume(g_arr); // Printing total volume System.out.println( "The total volume is " + vol); } } |
The total volume is 1840.6548245743668
Output Explanation:
The Application class is closed for modification. Please note there may be other ways to achieve the Open Close principle–ours is just one of the possible approaches.
On taking an overview, we found that our first approach wasn’t open for extension and required modification in the code to accommodate new requirements (new geometric objects) . While the second approach was open for extension and adding new requirements can be done without modifying any existing code. The second approach helps to achieve robustness in the whole program.