Skip to content

Understanding Open-Close Principle

The Open-Close Principle (OCP) is one of the SOLID principles of object-oriented design. It was introduced by Bertrand Meyer in his book “Object-Oriented Software Construction” in 1988.

The principle states:

Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification.

What does it mean?

Benefits of Open-Close Principle

  1. Flexibility: The Open-Close Principle allows for easy modification and extension of existing code without the need to change the original code. This makes the system more adaptable to changes and easier to maintain.

  2. Reusability: By adhering to the Open-Close Principle, we can reuse existing code and extend it to create new functionality, rather than writing new code from scratch. This saves time and reduces the risk of errors.

  3. Testability: The Open-Close Principle makes the code more testable. By isolating the behavior we want to test, we can ensure that it works as expected and that any changes to the code do not affect the test results.

Example which violates Open-Close Principle

Consider a shape-drawing system that initially supports drawing rectangles. Now, if you want to add support for drawing circles, you’d have to modify the ShapeDrawer class, which violates the Open/Closed Principle.


class ShapeDrawer {
  drawRectangle(width: number, height: number) {
    console.log(`Drawing a rectangle with width ${width} and height ${height}`);
  }
}

const drawer = new ShapeDrawer();
drawer.drawRectangle(10, 20);

Example which follows Open-Close Principle

To follow the OCP, we can use an interface for shapes and make the ShapeDrawer class work with any shape, which will allow us to add new shapes without modifying the class.

// Define a Shape interface
interface Shape {
  draw(): void;
}

// Implement Rectangle shape
class Rectangle implements Shape {
  constructor(private width: number, private height: number) {}

  draw() {
    console.log(`Drawing a rectangle with width ${this.width} and height ${this.height}`);
  }
}

// Implement Circle shape
class Circle implements Shape {
  constructor(private radius: number) {}

  draw() {
    console.log(`Drawing a circle with radius ${this.radius}`);
  }
}

// ShapeDrawer doesn't need to know the specific type of shapes
class ShapeDrawer {
  drawShape(shape: Shape) {
    shape.draw();
  }
}

// Usage
const drawer = new ShapeDrawer();
const rectangle = new Rectangle(10, 20);
const circle = new Circle(15);

drawer.drawShape(rectangle);
drawer.drawShape(circle);

Explanation:

By following this approach, you adhere to the Open/Closed Principle, ensuring that your system can be extended with new functionality without modifying existing, stable code.