Depinde de abstracții
Principiul inversării dependențelor spune: codul de nivel înalt — logica de business — nu trebuie să depindă de detalii de nivel jos, cum ar fi o bază de date anume sau un format de fișier. Ambele trebuie să depindă de o abstracție: în PHP, o interfață.
„Inversarea” este direcția săgeții. În loc ca serviciul să indice un depozit concret, serviciul indică o interfață — iar fiecare depozit indică aceeași interfață din partea cealaltă.
Sudat la detalii
Acest serviciu de utilizatori creează și folosește direct un DiskStore concret. Funcționează — dar serviciul este sudat la un singur depozit. Un alt depozit, un depozit mai rapid, un depozit fals pentru teste: fiecare dintre ele înseamnă modificarea serviciului însuși.
Serviciul sudat la depozitul său
UserService își construiește în interior propriul DiskStore. Logica de înregistrare, de nivel înalt, depinde acum de un detaliu de nivel jos — fișierul de pe disc.
Ieșire: script PHP
<?php
class DiskStore {
public function save(string $user): void {
echo "se scrie $user în fișierul users.txt pe disc\n";
}
}
// UserService își construiește propriul depozit — sudat la detaliu.
class UserService {
private DiskStore $store;
public function __construct() {
$this->store = new DiskStore();
}
public function register(string $user): void {
echo "se înregistrează utilizatorul: $user\n";
$this->store->save($user);
}
}
$service = new UserService();
$service->register("ana");
Inversează dependența
Soluția: serviciul cere o interfață Storage în constructorul său și nu află niciodată pe care a primit-o. Depozitul de pe disc și depozitul din memorie implementează amândouă interfața.
Acum săgeata indică din ambele părți spre abstracție — iar schimbarea depozitului este o modificare de o linie în locul unde se creează serviciul, nu în interiorul lui.
Serviciul acceptă o interfață
Aceeași logică de înregistrare, două depozite diferite. new UserService(new DiskStore()) sau new UserService(new MemoryStore()) — codul serviciului nu se schimbă niciodată.
Ieșire: script PHP
<?php
// Abstracția de care depind ambele părți.
interface Storage {
public function save(string $user): void;
}
class DiskStore implements Storage {
public function save(string $user): void {
echo "se scrie $user în fișierul users.txt pe disc\n";
}
}
class MemoryStore implements Storage {
public function save(string $user): void {
echo "se păstrează $user în memorie\n";
}
}
class UserService {
public function __construct(private Storage $store) {
}
public function register(string $user): void {
echo "se înregistrează utilizatorul: $user\n";
$this->store->save($user);
}
}
$onDisk = new UserService(new DiskStore());
$onDisk->register("ana");
$inMemory = new UserService(new MemoryStore());
$inMemory->register("ben");
Câștigul: testare fără disc
Pentru că serviciul depinde de abstracție, un test îi poate transmite un depozit din 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: script PHP
<?php
interface Storage {
public function save(string $user): void;
}
// MemoryStore ține evidența a ceea ce s-a salvat — perfect pentru verificări.
class MemoryStore implements Storage {
public array $users = [];
public function save(string $user): void {
$this->users[] = $user;
}
}
class UserService {
public function __construct(private Storage $store) {
}
public function register(string $user): void {
$this->store->save($user);
}
}
$store = new MemoryStore();
$service = new UserService($store);
$service->register("ana");
$service->register("ben");
if (count($store->users) === 2) {
echo "verificare trecută: 2 utilizatori salvați\n";
}
echo "utilizatori salvați: ", implode(", ", $store->users), "\n";