¿Qué es?
El Open/Closed Principle dice que debería poder agregar comportamiento nuevo a un sistema sin modificar código que ya funciona. En la práctica, esto se logra apoyándose en abstracciones (interfaces, polimorfismo) en vez de en condicionales que revisan tipos concretos.
Antes: hay que editar la clase por cada caso nuevo
type ShapeType = 'circle' | 'square';
interface Shape {
type: ShapeType;
radius?: number;
side?: number;
}
class AreaCalculator {
calculate(shape: Shape): number {
if (shape.type === 'circle') {
return Math.PI * shape.radius! ** 2;
} else if (shape.type === 'square') {
return shape.side! ** 2;
}
// Cada figura nueva obliga a tocar este método
throw new Error('Figura no soportada');
}
}Cada vez que aparece una figura nueva (triángulo, rectángulo...) hay que volver a abrir AreaCalculator y agregar otro else if, con el riesgo de romper los casos que ya funcionaban.
Después: extensión mediante polimorfismo
interface Shape {
area(): number;
}
class Circle implements Shape {
constructor(private radius: number) {}
area(): number {
return Math.PI * this.radius ** 2;
}
}
class Square implements Shape {
constructor(private side: number) {}
area(): number {
return this.side ** 2;
}
}
// Figura nueva: se agrega una clase, no se toca ninguna existente
class Triangle implements Shape {
constructor(private base: number, private height: number) {}
area(): number {
return (this.base * this.height) / 2;
}
}
class AreaCalculator {
calculateTotal(shapes: Shape[]): number {
return shapes.reduce((total, shape) => total + shape.area(), 0);
}
}AreaCalculator ya no conoce los tipos concretos: solo depende de la abstracción Shape. Agregar Triangle no requiere tocar ni una línea de las clases existentes.
A tener en cuenta: OCP no prohíbe modificar código nunca; prohíbe que agregar un caso nuevo obligue a reabrir y arriesgar código que ya está probado y funcionando en producción.