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
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)