SOLID- Open Closed Principle (OCP)

Share

The principle states that,

software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

The principle says that for every new functionality to be added, you should limit yourself for editing the existing modules (classes), rather you should always write a new class for every new functionality by extending the existing classes. This is one of the simplest SOLID principles, easy to understand and implement with great design benefits.

In simple terms, the principle talks about the usage of Abstract classes to achieve this. The direct benefits you get by following the principle are-

1. Since, you are not editing the existing classes and for any new functionality to be added, you would add a new class- you do not need to test the already written class. If you would have edited the already written class, you would have to test the new functionality and already existed old functionality in the class. So, following this, you would reduce huge testing efforts.

2. Your code would be more fragile i.e you can easily accommodate new functionality(ies) in your project.

Its very important to understand about how you can achieve the principle through the usage of Abstract classes. Following example would explain all about OCP.

Explanation:

Let us go through the below example to understand the principle and the underlying benefits: Suppose for a certain requirement you would need to write a class for calculating the fare that a person is charged with. So, you write the following two classes.


public class Bus {

	public double CostPerMile;
	public double MilesTravelled;
	public int NoOfSeatsBooked;
}

public class FareCalculator {

	public double Fare(Bus bus) {

		double fare = 0;
		fare = bus.CostPerMile * bus.MilesTravelled * bus.NoOfSeatsBooked;
		return fare;
	}
}

Everything is fine in the above code. FareCalculator class would return the computed fare for Bus properly.

But the above design is a clear violation of OCP: Let’s say, the new requirement says that the  FareCalculator class should include fare calculation for a new vehicle – Truck also. And, you know that the calculation would differ in both the cases. In case of Bus, the calculation depends on the number of seats booked and in case of Truck, it depends on the Weight of Goods that truck would be carrying.

So, you would modify FareCalculator class as follows:


public class FareCalculator {

	public double BusFare(Bus bus) {

		double fare = 0;
		fare = bus.CostPerMile * bus.MilesTravelled * bus.NoOfSeatsBooked;
		return fare;
	}

	public double TruckFare(Truck truck) {

		double fare = 0;
		fare = truck.CostPerMile * truck.MilesTravelled * truck.GoodsWeight;
		return fare;
	}
}

public class Bus {

	public double CostPerMile;
	public double MilesTravelled;
	public int NoOfSeatsBooked;
}

public class Truck {

	public double CostPerMile;
	public double MilesTravelled;
	public int GoodsWeight;
}

Definitely your Code would work fine and delivers the results accurately.

But, there are numerous design problems with the above modification:

If the requirement is to compute fare for one more vehicle say- Car etc, you would be making a change in the existing FareCalculator class to implement the new requirement. So, every time you need to make sure that the new function added in the class has not broken any older functinality present already in the class.

So, in the above design, its very clear that the class is not really closed for modifcation and also not open for extension. In the real world, the class may not be having just a few lines of code and in actual would be having hundreds or thousands lines of code. So, anything that you are modifying in an existing  class in order to implement a new functionality lead to enormous development and testing efforts. For every new functionality to be added, you need to make sure that nothing has been broken with respect to the functinality already present in the class.

You can overcome the said problems by following OCP:

You create a Abstract class Vehicle as follows

public abstract class Vehicle
{
  public double Fare();
}

And, then write Bus and Truck class extending the Vehicle class and implementing the Fare function as follows:

public class Bus extends Vehicle {

	public double CostPerMile;
	public double MilesTravelled;
	public int NoOfSeatsBooked;

	public double Fare() {

		return CostPerMile * MilesTravelled * NoOfSeatsBooked;
	}
}

public class Truck extends Vehicle {

	public double CostPerMile;
	public double MilesTravelled;
	public int GoodsWeight;

	public double Fare() {

		return CostPerMile * MilesTravelled * GoodsWeight;
	}
}

Finally, you write FareCalculator as follows:


public class FareCalculator {

	public double Fare(Vehicle vehicle) {

		double fare = 0;
		fare = vehicle.Fare();
		return fare;
	}
}

In the above design, the FareCalculator and all other classes are absolutely closed for modifications. With the usage of Abstract class  Vehicle, now, even if the requirement is to compute fare for a new vehicle, you can easily do it by writing a new class and extending the Vehicle class in it.  By doing so, you would not really need to make modifications in any of the existing classes and you will always extend the existing classes to implement any new functionality.

Conclusion, By following OCP, you would make your code more fragile and adds value towards the faster development life cycles.