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, care funcționează și este testat, nu se editează pentru asta.
Fiecare modificare adusă codului care funcționează 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.
Unealta lui Go pentru asta este interfața: o funcție care depinde de o interfață nu trebuie schimbată niciodată atunci când un tip nou o implementează.
Instrucțiunea switch care nu se mai oprește din creștere
Acest calculator de arii folosește un switch pe felul formei. Funcționează — până sosește 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 felul formei
totalArea trebuie să cunoască fiecare fel de formă. Adăugarea uneia înseamnă modificarea acestei funcții testate — exact lucrul împotriva căruia ne avertizează principiul.
Ieșire: program Go și rezultatul capturat
package main
import (
"fmt"
"math"
)
type shape struct {
kind string
width, height float64
radius float64
}
// totalArea trebuie să cunoască fiecare fel de formă — și crește cu fiecare formă nouă.
func totalArea(shapes []shape) float64 {
total := 0.0
for _, s := range shapes {
switch s.kind {
case "cerc":
total += math.Pi * s.radius * s.radius
case "dreptunghi":
total += s.width * s.height
}
}
return total
}
func main() {
shapes := []shape{
{kind: "cerc", radius: 2},
{kind: "dreptunghi", width: 3, height: 4},
}
fmt.Printf("aria totală: %.2f\n", totalArea(shapes))
}
Extinderea 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 sunt formele.
Acum privește principiul în acțiune: 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 Go și rezultatul capturat
package main
import (
"fmt"
"math"
)
type Shape interface {
Area() float64
}
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
// totalArea depinde doar de interfață — fără switch, nimic de editat.
func totalArea(shapes []Shape) float64 {
total := 0.0
for _, s := range shapes {
total += s.Area()
}
return total
}
func main() {
shapes := []Shape{
Circle{Radius: 2},
Rectangle{Width: 3, Height: 4},
}
fmt.Printf("aria totală: %.2f\n", totalArea(shapes))
}
Adaugi o formă nouă, nu atingi nimic
Triangle este cod pur nou: un tip și metoda sa Area(). totalArea, Circle și Rectangle sunt exact cum erau — deschise pentru extindere, închise pentru modificare.
Ieșire: program Go și rezultatul capturat
package main
import (
"fmt"
"math"
)
type Shape interface {
Area() float64
}
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
// Triangle este cod NOU — tot ce se află mai sus rămâne neatins.
type Triangle struct {
Base, Height float64
}
func (t Triangle) Area() float64 {
return t.Base * t.Height / 2
}
func totalArea(shapes []Shape) float64 {
total := 0.0
for _, s := range shapes {
total += s.Area()
}
return total
}
func main() {
shapes := []Shape{
Circle{Radius: 2},
Rectangle{Width: 3, Height: 4},
Triangle{Base: 6, Height: 5},
}
fmt.Printf("aria totală: %.2f\n", totalArea(shapes))
}