Solid Principles
In this tutorial we are going to learn about the Open–Closed Principle (OCP).
Reference
Open–Closed Principle (OCP) states that a class should be open for extension but closed for modification.
The open part of this principle implies that the class should be easily extendable. The closed part of this principle implies that the class should not be modified once it is ready.
Consider the following Java program. We have a AreaCalculator class which computes the area of different shapes based on their types and then adds them together to return the total area.
import java.util.List;
class Square {
private final double side;
public Square(double side) {
this.side = side;
}
public double getSide() {
return side;
}
}
class Rectangle {
private final double length;
private final double width;
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
public double getLength() {
return length;
}
public double getWidth() {
return width;
}
}
class Triangle {
private final double base;
private final double height;
public Triangle(double base, double height) {
this.base = base;
this.height = height;
}
public double getBase() {
return base;
}
public double getHeight() {
return height;
}
}
class AreaCalculator {
private final List<Object> shapes;
public AreaCalculator(List<Object> shapes) {
this.shapes = shapes;
}
public double totalArea() {
double total = 0;
for(Object shape: shapes) {
if (shape instanceof Square) {
Square square = (Square) shape;
total += square.getSide() * square.getSide();
}
else if (shape instanceof Rectangle) {
Rectangle rectangle = (Rectangle) shape;
total += rectangle.getLength() * rectangle.getWidth();
}
else if (shape instanceof Triangle) {
Triangle triangle = (Triangle) shape;
total += 0.5 * triangle.getBase() * triangle.getHeight();
}
}
return total;
}
}
Test
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
public class AreaCalculatorTests {
@Test
@DisplayName("Total Area")
public void shouldBeAbleToReturnTotalAreaOfAllTheShapes() {
ArrayList<Object> shapes = new ArrayList<>();
Square square = new Square(10);
shapes.add(square);
Rectangle rectangle = new Rectangle(10, 20);
shapes.add(rectangle);
Triangle triangle = new Triangle(5, 10);
shapes.add(triangle);
Assertions.assertEquals(new AreaCalculator(shapes).totalArea(), 325);
}
}
In the current implementation of the AreaCalculator class we are computing the area of different shapes after checking the type of the shape using if-else
statements. If we want to add new shapes like Circle, Octagon, etc. then we would have to open this class and make modifications and this would clearly break the Open-Closed Principle.
We can make the AreaCalculator class Open-Closed compliant by moving the area computation logic inside the respective classes. For this we can create a Shape
interface which will get implemented by the different shape classes like Square
, Triangle
, etc.
import java.util.List;
interface Shape {
public double getArea();
}
class Square implements Shape {
private final double side;
public Square(double side) {
this.side = side;
}
public double getArea() {
return side * side;
}
}
class Rectangle implements Shape {
private final double length;
private final double width;
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
public double getArea() {
return length * width;
}
}
class Triangle implements Shape {
private final double base;
private final double height;
public Triangle(double base, double height) {
this.base = base;
this.height = height;
}
public double getArea() {
return 0.5 * base * height;
}
}
class AreaCalculator {
private final List<Shape> shapes;
public AreaCalculator(List<Shape> shapes) {
this.shapes = shapes;
}
public double totalArea() {
double total = 0;
for(Shape shape : shapes) {
total += shape.getArea();
}
return total;
}
}
Test
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
public class AreaCalculatorTests {
@Test
@DisplayName("Total Area")
public void shouldBeAbleToReturnTotalAreaOfAllTheShapes() {
ArrayList<Shape> shapes = new ArrayList<>();
Square square = new Square(10);
shapes.add(square);
Rectangle rectangle = new Rectangle(10, 20);
shapes.add(rectangle);
Triangle triangle = new Triangle(5, 10);
shapes.add(triangle);
Assertions.assertEquals(new AreaCalculator(shapes).totalArea(), 325);
}
}
ADVERTISEMENT