Getty Images/iStockphoto

Tip

How to apply the open-closed principle in Java

How does the open-closed SOLID principle work in a Java program? Here we show you what this important principle means, and how to implement the open-closed principle in Java.

The SOLID open-closed principle in Java asserts that a well-designed software component will be open for ongoing extension, but closed to edits and modification.

In other words, a Java class that supports the SOLID open-closed principle will find use in originally un-envisioned scenarios, and be flexible enough to work with newly created classes, interfaces and Java records with no changes to the component's source code.

Developers often stumble over the open-closed principle in Java. The original definition uses the word extension, which in Java implies inheritance. However, inheritance isn't the best way to implement the open-closed principle in Java.

Open-closed principle example

Here is a simple example of a class that fails to implement the open-closed principle properly.

Imagine a program that has two instances of a square and needs a custom component to compare their area. The code would look like the following:

class Square() {
  int height;
  int area() { return height * height; }
}

public class OpenOpenExample {

  public int compareArea(Square a, Square b) {
    return a.area() - b.area();
  } 

}

The issue with the OpenOpenExample isn't necessarily obvious.

For this specific use case, the OpenOpenExample works perfectly well. It returns zero if the two squares are the same size, a positive number if the first square is larger and a negative number if the first square is smaller.

However, a problem quickly arises when a circle is brought into the mix.

The extension problem

To compare the area of two circles, we must modify the OpenOpenExample. However, this violates the SOLID open-closed principle.

The following example updates the OpenOpenExample class to provide support for circles:

class Circle {
  int r;
  int area() { return Math.PI*r*r*;}
}


class OpenOpenExample {

  public int compareArea(Square a, Square b) {
    return a.area() - b.area();
  }

  public int compareArea(Circle x, Circle y) {
   return x.area() - y.area();
  }

}

You can easily imagine the OpenOpenExample growing larger and larger as more shapes are introduced into the problem domain.

Applying the open-closed principle in Java

The addition of an interface to our example helps to overcome the violation of the open-closed principle. An interface allows for infinite future extensions with no need to ever edit the class again.

To fix this example, we first create an interface that both the circle and the square implement:

interface Shape {
  int area();
} 

class Circle implements Shape {
  int r;
  int area() { return Math.PI*r*r*;}
}

class Square() implements {
  int height;
  int area() { return height * height; }
}

We then create a new class named OpenClosedExample which has a single compareArea method that uses the Shape interface as arguments:

public class OpenClosedExample {

  public int compareArea(Shape a, Shape b) {
    return a.area() - b.area();
  }

}

With this small change applied, you can pass any class that implements the Shape interface as an argument to the OpenClosedExample's compareArea method.

This makes the class extensible to an infinite number of Shape classes that could potentially be created.

SOLID principles applied

Furthermore, the OpenClosedExample supports all of these new classes with no edits to the source code.

In other words, this well-designed Java component will be open for extension but closed to edits and modification. And that's the official definition of the SOLID open-closed principle.

Open-closed principle in SOLID.
The open-closed concept is one of the five SOLID principles in object-oriented programming.

Dig Deeper on Core Java APIs and programming techniques