Deschis pentru extindere, închis pentru modificare
Principiul Deschis/închis 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 editare a unui cod funcțional este o șansă 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.
Instrumentul PHP pentru asta este interfața: o funcție care depinde de o interfață nu are nevoie niciodată de modificări atunci când o clasă nouă o implementează.
Switch-ul care nu se oprește niciodată din creștere
Acest calculator de arie folosește un switch pe felul formei. Funcționează — până apare următoarea formă. Triunghi? Editează switch-ul. Pentagon? Editează-l din nou. Funcția nu este niciodată terminată, iar fiecare editare pune în pericol formele care funcționau deja.
Un switch pe felul formei
totalArea trebuie să cunoască fiecare fel de formă. Adăugarea unuia nou înseamnă modificarea acestei funcții testate — exact lucrul împotriva căruia avertizează principiul.
Ieșire: script PHP
<?php
class Shape {
public function __construct(
public string $kind,
public float $width = 0,
public float $height = 0,
public float $radius = 0,
) {
}
}
// totalArea trebuie să cunoască fiecare fel de formă — și crește cu fiecare formă nouă.
function totalArea(Shape ...$shapes): float {
$total = 0.0;
foreach ($shapes as $s) {
switch ($s->kind) {
case "circle":
$total += M_PI * $s->radius * $s->radius;
break;
case "rectangle":
$total += $s->width * $s->height;
break;
}
}
return $total;
}
$total = totalArea(
new Shape(kind: "circle", radius: 2),
new Shape(kind: "rectangle", width: 3, height: 4),
);
printf("arie totală: %.2f\n", $total);
Extinde prin intermediul unei interfețe
Soluția: fiecare formă își cunoaște propria arie. Interfața Shape cere o singură metodă, area(), iar totalArea doar adună ceea ce primește — nu îi mai pasă ce fel de forme sunt.
Acum privește principiul la lucru: o clasă Triangle complet nouă se conectează, iar totalArea nu este atinsă. Extindere fără modificare.
Fiecare formă își calculează propria arie
totalArea depinde numai de interfața Shape. Switch-ul a dispărut — și cu el, nevoia de a mai edita vreodată această funcție.
Ieșire: script PHP
<?php
interface Shape {
public function area(): float;
}
class Circle implements Shape {
public function __construct(public float $radius) {
}
public function area(): float {
return M_PI * $this->radius * $this->radius;
}
}
class Rectangle implements Shape {
public function __construct(public float $width, public float $height) {
}
public function area(): float {
return $this->width * $this->height;
}
}
// totalArea depinde numai de interfață — fără switch, nimic de editat.
function totalArea(Shape ...$shapes): float {
$total = 0.0;
foreach ($shapes as $s) {
$total += $s->area();
}
return $total;
}
$total = totalArea(
new Circle(radius: 2),
new Rectangle(width: 3, height: 4),
);
printf("arie totală: %.2f\n", $total);
Adaugă o formă nouă, fără să atingi nimic
Triangle este cod pur nou: o clasă care implementează area(). totalArea, Circle și Rectangle sunt exact cum erau — deschise pentru extindere, închise pentru modificare.
Ieșire: script PHP
<?php
interface Shape {
public function area(): float;
}
class Circle implements Shape {
public function __construct(public float $radius) {
}
public function area(): float {
return M_PI * $this->radius * $this->radius;
}
}
class Rectangle implements Shape {
public function __construct(public float $width, public float $height) {
}
public function area(): float {
return $this->width * $this->height;
}
}
// Triangle este cod NOU — tot ce este mai sus rămâne neatins.
class Triangle implements Shape {
public function __construct(public float $base, public float $height) {
}
public function area(): float {
return $this->base * $this->height / 2;
}
}
function totalArea(Shape ...$shapes): float {
$total = 0.0;
foreach ($shapes as $s) {
$total += $s->area();
}
return $total;
}
$total = totalArea(
new Circle(radius: 2),
new Rectangle(width: 3, height: 4),
new Triangle(base: 6, height: 5),
);
printf("arie totală: %.2f\n", $total);