Interfețe mici
Principiul segregării interfețelor spune: nicio clasă nu trebuie să fie obligată să implementeze metode de care nu are nevoie. În loc de o interfață mare, cu multe metode, creează mai multe interfețe mici — fiecare clasă implementează doar ceea ce este adevărat pentru ea.
În Python, o interfață se scrie ca o clasă protocol, iar protocoalele mici sunt foarte naturale aici: limbajul însuși funcționează pe baza unora minuscule — orice obiect cu o metodă __next__() funcționează cu funcția încorporată next(), iar o mare parte din biblioteca standard se bazează pe promisiuni la fel de mici.
Interfața mare
Un protocol Worker cu work() și eat() pare inofensiv — până când un robot aplică pentru post. Robotul lucrează bine, dar trebuie să implementeze și eat(), cu... ceva. Răspunsurile absurde de acest fel sunt semnul unei interfețe mari.
Robotul obligat să mănânce
Robotul implementează eat() doar pentru că protocolul o cere. Pauza de prânz afișează un răspuns care n-ar trebui să existe — clasa a fost obligată să mintă.
Ieșire: program Python complet
from typing import Protocol
# Un singur protocol mare — fiecare lucrător TREBUIE să și mănânce.
class Worker(Protocol):
def work(self) -> str: ...
def eat(self) -> str: ...
class Human:
def work(self) -> str:
return "om: lucrează"
def eat(self) -> str:
return "om: ia prânzul"
class Robot:
def work(self) -> str:
return "robot: lucrează"
# Impus de protocol — roboții nu mănâncă.
def eat(self) -> str:
return "robot: EROARE, nu poate mânca"
team: list[Worker] = [Human(), Robot()]
for w in team:
print(w.work())
for w in team:
print(w.eat())
Împarte interfața
Împarte protocolul mare în Worker și Eater. Omul le implementează pe amândouă; robotul implementează doar Worker. Tura de lucru primește lucrători, pauza de prânz primește mâncători — robotul pur și simplu nu este invitat niciodată la prânz și nicio metodă nu minte.
Fiecare clasă implementează doar adevărul ei
Două protocoale mici în loc de unul mare. Robotul nu mai are deloc metoda eat() — iar un verificator de tipuri (de exemplu, mypy) îl ține departe de pauza de prânz.
Ieșire: program Python complet
from typing import Protocol
# Două protocoale mici — fiecare clasă implementează doar adevărul ei.
class Worker(Protocol):
def work(self) -> str: ...
class Eater(Protocol):
def eat(self) -> str: ...
class Human:
def work(self) -> str:
return "om: lucrează"
def eat(self) -> str:
return "om: ia prânzul"
class Robot:
def work(self) -> str:
return "robot: lucrează"
def work_shift(team: list[Worker]) -> None:
for w in team:
print(w.work())
def lunch_break(guests: list[Eater]) -> None:
for e in guests:
print(e.eat())
work_shift([Human(), Robot()])
lunch_break([Human()])
Piesele mici se combină
Protocoalele mici nu sunt o limitare — ele se combină. Când o funcție chiar are nevoie de ambele abilități, le combină pe loc într-un singur protocol: Worker plus Eater.
Așa cresc promisiunile mici: definește protocoale cu o singură metodă și lasă-l pe cel care are nevoie de mai mult să le unească.
Combină atunci când ai nevoie de ambele
WorkingEater este construit din cele două protocoale mici. Tura de o zi întreagă are nevoie de ambele abilități, deci doar omul se califică — spus o singură dată, în sistemul de tipuri.
Ieșire: program Python complet
from typing import Protocol
class Worker(Protocol):
def work(self) -> str: ...
class Eater(Protocol):
def eat(self) -> str: ...
# Protocoalele mici se combină în unele mai mari atunci când este nevoie.
class WorkingEater(Worker, Eater, Protocol):
pass
class Human:
def work(self) -> str:
return "om: lucrează"
def eat(self) -> str:
return "om: ia prânzul"
def full_day(we: WorkingEater) -> None:
print(we.work())
print(we.eat())
print(we.work())
full_day(Human())