Depinde de abstracțiuni
Principiul inversării dependențelor spune: codul de nivel înalt — logica de business — nu trebuie să depindă de detalii de nivel scăzut, cum ar fi o anumită bază de date sau un anumit format de fișier. Ambele trebuie să depindă de o abstracțiune: în Python, un protocol.
„Inversarea” se referă la direcția săgeții. În loc ca serviciul să indice spre o stocare concretă, serviciul indică spre un protocol — iar fiecare stocare indică spre același protocol din partea cealaltă.
Legat direct de detalii
Acest serviciu de utilizatori creează și folosește direct un DiskStore concret. Funcționează — dar serviciul este sudat de o singură stocare. O altă stocare, o stocare mai rapidă, o stocare falsă pentru teste: fiecare dintre ele înseamnă modificarea serviciului însuși.
Serviciul sudat de stocarea sa
UserService își construiește singur propriul DiskStore în interior. Logica de nivel înalt pentru înregistrare depinde acum de un detaliu de nivel scăzut — fișierul de pe disc.
Ieșire: program Python complet
class DiskStore:
def save(self, user):
print("se scrie utilizatorul", user, "în fișierul users.txt pe disc")
# UserService își construiește singur stocarea — sudat de detaliu.
class UserService:
def __init__(self):
self.store = DiskStore()
def register(self, user):
print("se înregistrează utilizatorul:", user)
self.store.save(user)
service = UserService()
service.register("ana")
Inversează dependența
Soluția: serviciul cere un protocol Storage în constructorul său și nu află niciodată ce stocare a primit. Atât stocarea pe disc, cât și stocarea în memorie implementează protocolul.
Acum săgeata indică din ambele părți spre abstracțiune — iar înlocuirea stocării este o modificare de o singură linie în locul unde este creat serviciul, nu în interiorul lui.
Serviciul acceptă un protocol
Aceeași logică de înregistrare, două stocări diferite. UserService(DiskStore()) sau UserService(MemoryStore()) — codul serviciului însuși nu se schimbă niciodată.
Ieșire: program Python complet
from typing import Protocol
# Abstracțiunea de care depind ambele părți.
class Storage(Protocol):
def save(self, user: str) -> None: ...
class DiskStore:
def save(self, user: str) -> None:
print("se scrie utilizatorul", user, "în fișierul users.txt pe disc")
class MemoryStore:
def save(self, user: str) -> None:
print("se păstrează utilizatorul", user, "în memorie")
class UserService:
def __init__(self, store: Storage):
self.store = store
def register(self, user: str) -> None:
print("se înregistrează utilizatorul:", user)
self.store.save(user)
on_disk = UserService(DiskStore())
on_disk.register("ana")
in_memory = UserService(MemoryStore())
in_memory.register("ben")
Câștigul: testare fără disc
Deoarece serviciul depinde de abstracțiune, un test îi poate da o stocare în memorie și poate verifica rezultatul instantaneu — fără fișiere, fără curățenie. Inversarea dependențelor este ceea ce face codul ușor de testat unitar.
Ieșire: program Python complet
from typing import Protocol
class Storage(Protocol):
def save(self, user: str) -> None: ...
# MemoryStore reține ce a fost salvat — perfect pentru verificări.
class MemoryStore:
def __init__(self):
self.users: list[str] = []
def save(self, user: str) -> None:
self.users.append(user)
class UserService:
def __init__(self, store: Storage):
self.store = store
def register(self, user: str) -> None:
self.store.save(user)
store = MemoryStore()
service = UserService(store)
service.register("ana")
service.register("ben")
if len(store.users) == 2:
print("verificare reușită: 2 utilizatori salvați")
print("utilizatori salvați:", store.users)