23May
Open-Closed Principle

Software applications are changing during both development and maintenance phases. New functionalities are usually added but also changes to existing functionalities are implemented. When this happens, what is important is to minimize the amount of changes to existing code. Open-Closed Principle (OCP) gives an approach on how to do this: software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

When a single change to the application code generates a cascade of changes to dependent modules, it is a sign of “bad” design. The Open-Closed Principle interpretation is: you should never change the existing design, you should always extend the behaviour of such modules, by adding new ode, not changing existing code that already works.

How can you do that? The answer is using abstractions. Through abstractions you can represent an unbounded group of possible behaviours. In the same time, they are fixed and might contain common aspects to the behaviours. A module that interacts with an abstraction is closed for modification because the abstraction is fixed, however its behaviour can be extended by creating new behaviours based on the abstraction.

In Object Oriented Design, an abstraction is represented by an interface or an abstract class. While the definition of the interface or abstract class is fixed (methods and attributes are declared/defined from the beginning), the implementation of the abstract methods through derivate classes are generating new behaviours.

Let’s look at an example. We have a set of geometrical shapes for which we need to calculate the area. The shapes can circles, squares, rectangles, etc. The way area is calculated for each one of them is different; this means different behaviours. How we can model this?

class abstract Shape {
public abstract double area( );
}

class Circle extends Shape {

private int radius;

@Override
public double area() {
return Math.PI * radius * radius;
}

}

class Square extends Shape {

private double side;

@Override
public double area() {
return side * side;
}

}

According to OCP, you need to have an abstraction. So we define Shape class, as an abstraction of all geometrical shapes. The only fix element is the area calculation, so this will be a method declared inside the abstraction, which will be implemented by all concrete shapes.

Let’s assume you have a surface which is composed from different geometrical shapes, and you want to calculate the area of the whole surface. That is easy, you sum the area of all geometrical shapes.

public class Surface {

List shapes;

public double area( ) {
double area = 0;
for ( Shape shape: shapes ) {
area += shape.area( );
}
return area;
}

}

Is Surface class following OCP? For sure it does. It is closed for modification, the area will be always be calculated by summing up the areas of all shapes, and it doesn’t care how the area of each shape is calculated, that is hidden behind the abstraction. It is open for extension, since it can handle any Shape class (so if we encounter a Rectangle shape, we define it as derived from Shape class, and that is completely transparent for Surface class, as it only deals with the abstraction, not with the concrete implementations).

What if we will need, at some point in the future, to calculate the perimeter of a shape? Well, we add an abstract method to the Shape class, called perimeter( ), which will further be implemented in the derived classes. Is that a violation of OCP? Since we are changing the code of classes (you add a new method), you might see it like this, as you are editing the same source file. However, you add new code, you do not change existing code; existing code (area( ) method and the attributes) is not touched. Even this can be avoided if you anticipate, at design time, some of these possible future needs.

It is clear that you cannot follow 100% this principle, covering from the beginning all possible scenarios in which a module can be changed. You should consider those situations that are more likely and apply this principle in order to achieve a robust design. Do not consider any theoretical situation, but only those that, in the context of whole application and its purpose, make sense.

There is much more that could be said about the open-closed principle. In many ways this principle is at the heart of object oriented design. Conformance to this principle is what yields the greatest benefits claimed for object oriented technology like reusability and maintainability.