Deschis pentru extindere, închis pentru modificare
Principiul Open/Closed spune: codul trebuie să fie deschis pentru extindere — se poate adăuga comportament nou — dar închis pentru modificare — codul existent, funcțional și testat nu se editează pentru asta.
Fiecare modificare a unui cod care funcționează este o ocazie de a-l strica. Când adăugarea unei funcționalități înseamnă doar adăugarea de cod nou, codul vechi rămâne verificat.
Unealta TypeScript pentru asta este interfața: o funcție care depinde de o interfață nu trebuie schimbată niciodată când o clasă nouă o implementează.
Switch-ul care crește la nesfârșit
Acest calculator de arii folosește un switch pe tipul formei. Funcționează — până apare următoarea formă. Triunghi? Editezi switch-ul. Pentagon? Îl editezi din nou. Funcția nu este niciodată terminată, iar fiecare modificare pune în pericol formele care funcționau deja.
Un switch pe tipul formei
totalArea trebuie să cunoască fiecare tip de formă. Adăugarea uneia înseamnă modificarea acestei funcții testate — exact lucrul împotriva căruia avertizează principiul.
Ieșire: program TypeScript și rezultatul capturat
interface ShapeData {
kind: string
width: number
height: number
radius: number
}
// totalArea trebuie să cunoască fiecare kind — și crește cu fiecare formă nouă.
function totalArea(shapes: ShapeData[]): number {
let total = 0
for (const s of shapes) {
switch (s.kind) {
case "circle":
total += Math.PI * s.radius * s.radius
break
case "rectangle":
total += s.width * s.height
break
}
}
return total
}
const shapes: ShapeData[] = [
{ kind: "circle", width: 0, height: 0, radius: 2 },
{ kind: "rectangle", width: 3, height: 4, radius: 0 },
]
console.log("arie totală:", totalArea(shapes).toFixed(2))
Extinde printr-o interfață
Soluția: fiecare formă își cunoaște propria arie. Interfața Shape cere o singură metodă, area(), iar totalArea doar adună ce primește — nu îi mai pasă ce forme sunt.
Acum privește principiul la lucru: un Triangle complet nou se conectează, iar totalArea nu este atinsă. Extindere fără modificare.
Fiecare formă își calculează propria arie
totalArea depinde doar de interfața Shape. Switch-ul a dispărut — și odată cu el, nevoia de a mai edita vreodată această funcție.
Ieșire: program TypeScript și rezultatul capturat
interface Shape {
area(): number
}
class Circle implements Shape {
radius: number
constructor(radius: number) {
this.radius = radius
}
area(): number {
return Math.PI * this.radius * this.radius
}
}
class Rectangle implements Shape {
width: number
height: number
constructor(width: number, height: number) {
this.width = width
this.height = height
}
area(): number {
return this.width * this.height
}
}
// totalArea depinde doar de interfață — fără switch, nimic de editat.
function totalArea(shapes: Shape[]): number {
let total = 0
for (const s of shapes) {
total += s.area()
}
return total
}
const shapes: Shape[] = [new Circle(2), new Rectangle(3, 4)]
console.log("arie totală:", totalArea(shapes).toFixed(2))
Adaugă o formă nouă, fără să atingi nimic
Triangle este cod pur nou: o clasă și metoda ei area(). totalArea, Circle și Rectangle sunt exact cum erau — deschise pentru extindere, închise pentru modificare.
Ieșire: program TypeScript și rezultatul capturat
interface Shape {
area(): number
}
class Circle implements Shape {
radius: number
constructor(radius: number) {
this.radius = radius
}
area(): number {
return Math.PI * this.radius * this.radius
}
}
class Rectangle implements Shape {
width: number
height: number
constructor(width: number, height: number) {
this.width = width
this.height = height
}
area(): number {
return this.width * this.height
}
}
// Triangle este cod NOU — tot ce este mai sus rămâne neatins.
class Triangle implements Shape {
base: number
height: number
constructor(base: number, height: number) {
this.base = base
this.height = height
}
area(): number {
return (this.base * this.height) / 2
}
}
function totalArea(shapes: Shape[]): number {
let total = 0
for (const s of shapes) {
total += s.area()
}
return total
}
const shapes: Shape[] = [new Circle(2), new Rectangle(3, 4), new Triangle(6, 5)]
console.log("arie totală:", totalArea(shapes).toFixed(2))