Definition: The Open-Closed Principle (OCP) is a software design principle that states that software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.
This definition gave me jitters when I first heard it. Let’s pause for a bit, take a deep breath, try to process it slowly and possibly try not to cry – Software program entities (classes, modules, functions, etc.) should be open for expansion but closed for modification. According to this idea, new code should be added to modify or extend the behavior of a software component rather than changing the existing code. Let’s look at an example:
public class InputValidator {
public boolean validateInput(String input) {
// code to validate input
}
}
The class could have a single method, such as validateInput(), that takes in a String of user input and returns a boolean indicating whether or not the input is valid. InputValidator class is being used in many places. Now, we have a requirement of adding a new type of validation, such as ensuring that the input is a valid email address. If we were to change the logic of InputValidator, we have an additional responsibility of ensuring all other code points that access this method work as expected. Crucial point to note is that our change should not break the existing logic.
How do we proceed then? We could extend the class and add a new method, such as validateEmail(), without having to modify the existing validateInput() method. Something like the below snippet:
public class EmailValidator extends InputValidator {
public boolean validateInput(String input) {
// code to validate email
}
}
With this simple change we can be assured that the our new change doesn’t break the existing logic! validateInput() of InputValidator is closed for modification but open for extension.
While the example is good enough to explain the concept, better implementation could be something like:
public interface Validator<T> {
void validate(T input);
}
class InputValidator implements Validator<String>{
@Override
public void validate(String input) {
System.out.println("Validate Stirng input");
}
}
class OutputValidator implements Validator<BigDecimal>{
@Override
public void validate(BigDecimal output) {
System.out.println("Validate output");
}
}
Here, generic validator class can be used to validate any object and can be used to validate practically anything.
One other classic example could be of Shape abstract class that is open for extension, but closed for modification.
public abstract class Shape {
public abstract double getArea();
}
public class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
public double getArea() {
return Math.PI * radius * radius;
}
}
public class Rectangle extends Shape {
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
public double getArea() {
return width * height;
}
}
In this example, the abstract Shape class is closed for modification, as we are not able to modify its code. However, the class is open for extension, as we are able to create new classes that extend Shape, such as Circle and Rectangle, and add new functionality without modifying the existing code.
Advantages:
- Decreased Risk of Introducing Bugs or Breaking Existing Functionality: By adhering to the OCP, code updates can be implemented without altering existing code, decreasing the chance of introducing bugs or decreasing the likelihood of existing functionality being broken.
- More Maintainable and Flexible Code: Since code changes can be made without altering current code, the OCP aids in the creation of more maintainable and flexible code.
This makes refactoring and modification simpler. - Simplifying Development: Adhering to the OCP can simplify development because it eliminates the need to modify current code when adding new features or making adjustments.
Hope this helps! Thanks