Interfețe mici
Principiul segregării interfețelor spune: niciun tip nu ar trebui obligat să implementeze metode de care nu are nevoie. În loc de o singură interfață supraîncărcată, cu multe metode, fă mai multe interfețe mici — fiecare tip implementează doar ceea ce este adevărat pentru el.
Acesta este, dintre cele cinci, principiul cel mai apropiat de spiritul limbajului Go: biblioteca standard este construită din interfețe minuscule, cele mai cunoscute fiind cele cu o singură metodă pentru citire și scriere, la care se conectează jumătate din ecosistem.
Interfața supraîncărcată
O interfață Worker cu Work() și Eat() pare inofensivă — până când un robot își depune candidatura pentru post. Robotul lucrează bine, dar trebuie să implementeze și Eat(), cu... ceva. Răspunsurile fără sens ca acesta sunt semnătura unei interfețe supraîncărcate.
Robotul obligat să mănânce
Robotul implementează Eat() doar pentru că interfața o cere. Pauza de prânz afișează un răspuns care nu ar trebui să existe niciodată — tipul a fost obligat să mintă.
Ieșire: program Go și rezultatul capturat
package main
import "fmt"
// O singură interfață supraîncărcată — orice lucrător TREBUIE să și mănânce.
type Worker interface {
Work() string
Eat() string
}
type Human struct{}
func (h Human) Work() string { return "om: lucrează" }
func (h Human) Eat() string { return "om: mănâncă prânzul" }
type Robot struct{}
func (r Robot) Work() string { return "robot: lucrează" }
// Impusă de interfață — roboții nu mănâncă.
func (r Robot) Eat() string { return "robot: EROARE, nu poate mânca" }
func main() {
team := []Worker{Human{}, Robot{}}
for _, w := range team {
fmt.Println(w.Work())
}
for _, w := range team {
fmt.Println(w.Eat())
}
}
Împarte interfața
Împarte interfața supraîncărcată în Worker și Eater. Omul le implementează pe amândouă; robotul implementează doar Worker. Schimbul de lucru primește lucrători, pauza de prânz primește mâncători — robotul pur și simplu nu este niciodată invitat la prânz și nicio metodă nu minte.
Fiecare tip implementează doar adevărul său
Două interfețe mici în loc de una supraîncărcată. Robotul nu mai are deloc o metodă Eat() — compilatorul îl ține automat în afara pauzei de prânz.
Ieșire: program Go și rezultatul capturat
package main
import "fmt"
// Două interfețe mici — fiecare tip implementează doar ce este adevărat pentru el.
type Worker interface {
Work() string
}
type Eater interface {
Eat() string
}
type Human struct{}
func (h Human) Work() string { return "om: lucrează" }
func (h Human) Eat() string { return "om: mănâncă prânzul" }
type Robot struct{}
func (r Robot) Work() string { return "robot: lucrează" }
func workShift(team []Worker) {
for _, w := range team {
fmt.Println(w.Work())
}
}
func lunchBreak(guests []Eater) {
for _, e := range guests {
fmt.Println(e.Eat())
}
}
func main() {
workShift([]Worker{Human{}, Robot{}})
lunchBreak([]Eater{Human{}})
}
Piesele mici se compun
Interfețele mici nu sunt o limitare — ele se combină. Când o funcție chiar are nevoie de ambele capacități, le compune pe loc într-o singură interfață: Worker plus Eater.
Acesta este tiparul din biblioteca standard: definește interfețe cu o singură metodă și lasă-l pe cel care are nevoie de mai mult să le lipească împreună.
Compune atunci când ai nevoie de amândouă
WorkingEater este construită din cele două interfețe mici. Schimbul de o zi întreagă are nevoie de ambele capacități, așa că doar omul se califică — spus o singură dată, în sistemul de tipuri.
Ieșire: program Go și rezultatul capturat
package main
import "fmt"
type Worker interface {
Work() string
}
type Eater interface {
Eat() string
}
// Interfețele mici se combină în altele mai mari atunci când este nevoie.
type WorkingEater interface {
Worker
Eater
}
type Human struct{}
func (h Human) Work() string { return "om: lucrează" }
func (h Human) Eat() string { return "om: mănâncă prânzul" }
func fullDay(we WorkingEater) {
fmt.Println(we.Work())
fmt.Println(we.Eat())
fmt.Println(we.Work())
}
func main() {
fullDay(Human{})
}