The Open/Closed Principle (OCP) states that software entities (classes, modules, functions, etc.) should be open for extension but closed for modification. In other words, the behavior of a system can be extended without modifying its existing code.
Let's consider a practical use case to understand the OCP:
Use Case: Shape Drawing Application
Example: Suppose we are developing a shape drawing application that allows users to draw various shapes, such as rectangles, circles, and triangles, on a canvas. Each shape has its own rendering logic.
Without applying the OCP:In an initial implementation, we might have a single Shape class with a draw() method that contains the rendering logic for different shapes. However, this violates the OCP because adding a new shape would require modifying the existing Shape class.
With Applying the OCP:To apply the OCP, we can use abstraction and inheritance to separate the rendering logic for each shape into its own class, and provide a common interface or base class for drawing shapes.
Shape Interface:
public interface Shape {
void draw();
}
Rectangle, Circle and new Shape Triangle implementation classes :
public class Rectangle implements Shape {
@Override
public void draw() {
// rendering logic for drawing a rectangle
}
}
public class Circle implements Shape {
@Override
public void draw() {
// rendering logic for drawing a circle
}
}
// New shape class can be added without modifying existing code
public class Triangle implements Shape {
@Override
public void draw() {
// rendering logic for drawing a triangle
}
}
Canvas class:
public class Canvas {
private List<Shape> shapes;
public Canvas() {
shapes = new ArrayList<>();
}
public void addShape(Shape shape) {
shapes.add(shape);
}
public void drawShapes() {
for (Shape shape : shapes) {
shape.draw();
}
}
}
In this example, we have a Shape interface representing the common behavior for all shapes. Each shape class (Rectangle, Circle, Triangle) implements the Shape interface and provides its own implementation of the draw()method. This allows us to extend the behavior of the drawing application by adding new shape classes without modifying the existing code.
The Canvas class manages a list of shapes and provides methods to add shapes addShape() and draw all the shapes on the canvas drawShapes(). It depends on the Shape interface rather than specific shape classes. This adherence to abstraction and the use of an interface enables the application to be open for extension (new shapes can be added) but closed for modification (existing code doesn't need to change).
By following the OCP, the shape drawing application becomes more flexible and maintainable. Adding new shapes is as simple as creating a new class that implements the Shape interface and providing the rendering logic. The existing code remains untouched, promoting code reuse, modularity, and easier maintenance of the system.