Open/Closed Principle

Open for extension, closed for modification

Overview

The Open/Closed Principle (OCP) states that software entities (classes, modules, functions) should be open for extension but closed for modification. You should be able to add new functionality without changing existing code.

This is achieved through abstraction and polymorphism. New behavior is added by creating new classes that implement existing interfaces.

Key Concepts

Open for extension - can add new behavior

Closed for modification - don't change existing code

Achieved through abstraction (interfaces)

Use polymorphism to extend behavior

Reduces risk of breaking existing functionality

Code Example

java
1// ❌ BAD: Violates OCP - must modify code for new shapes
2class AreaCalculator {
3    public double calculateArea(Object shape) {
4        if (shape instanceof Circle) {
5            Circle circle = (Circle) shape;
6            return Math.PI * circle.radius * circle.radius;
7        } else if (shape instanceof Rectangle) {
8            Rectangle rect = (Rectangle) shape;
9            return rect.width * rect.height;
10        }
11        // Must add new else-if for every new shape!
12        // else if (shape instanceof Triangle) { ... }
13        return 0;
14    }
15}
16
17// ✅ GOOD: Follows OCP - extend without modifying
18interface Shape {
19    double calculateArea();
20}
21
22class Circle implements Shape {
23    private double radius;
24    
25    public Circle(double radius) {
26        this.radius = radius;
27    }
28    
29    @Override
30    public double calculateArea() {
31        return Math.PI * radius * radius;
32    }
33}
34
35class Rectangle implements Shape {
36    private double width, height;
37    
38    public Rectangle(double width, double height) {
39        this.width = width;
40        this.height = height;
41    }
42    
43    @Override
44    public double calculateArea() {
45        return width * height;
46    }
47}
48
49// Adding new shape - NO modification to existing code!
50class Triangle implements Shape {
51    private double base, height;
52    
53    public Triangle(double base, double height) {
54        this.base = base;
55        this.height = height;
56    }
57    
58    @Override
59    public double calculateArea() {
60        return 0.5 * base * height;
61    }
62}
63
64class AreaCalculator {
65    public double calculateTotalArea(List<Shape> shapes) {
66        double total = 0;
67        for (Shape shape : shapes) {
68            total += shape.calculateArea();  // Works for any shape!
69        }
70        return total;
71    }
72}

Adding Triangle doesn't require modifying AreaCalculator. New shapes just implement the Shape interface.

When to Use

  • When designing classes that will be extended

  • Building frameworks and libraries

  • When requirements are likely to change

  • Creating plugin architectures

Advantages

  • Reduces risk of bugs in existing code

  • Easier to add new features

  • Better separation of concerns

  • Enables plugin architectures

Best Practices

  • 1.

    Use interfaces to define abstractions

  • 2.

    Identify parts likely to change

  • 3.

    Use Strategy, Factory, or Decorator patterns

  • 4.

    Don't over-engineer - apply when needed

💡 Interview Tips

  • Start with the shape/area calculator example

  • Show before/after code

  • Explain how it reduces risk

  • Connect to Strategy pattern

  • Discuss real-world examples (plugins, payment gateways)

AI Tutor

Ask about the topic

Sign in Required

Please sign in to use the AI tutor

Sign In
Open/Closed Principle - SOLID Principles | LLD | Revise Algo