Dependency Inversion Principle (DIP), Inversion des dépendances
Principe
Une classe doit dépendre de son abstraction, pas de son implémentation.
Détail :
- Éssayer au maximum de typer des interfaces en paramètre plutôt que des objets directement.
- Les dépendances entre classes doivent se faire à travers des abstractions.
⚠️ Sans le principe du DIP
Dans l'exemple de code suivant, la classe ReportGenerator
est directement dépendante de la classe PDFExporter
, donc toute modification de la classe concrète PDFExporter
peut affecter la classe ReportGenerator
.
<?php
// Class concrète d'export d'un PDF
class PDFExporter
{
public function export(string $content): void
{
// Export du contenu en PDF
}
}
// Class concrète de génération d'un rapport
class ReportGenerator
{
private $exporter;
// ⚠️ Mauvaise pratique : Couplage fort entre class concrètes ReportGenerator et PDFExporter
public function __construct(PDFExporter $exporter)
{
$this->exporter = $exporter;
}
public function generateReport(): void
{
$content = "Report Content";
$this->exporter->export($content);
}
}
Les problèmes
la classe ReportGenerator
apporte une dépendance direct avec PDFExporter
introduisant un couplage fort entre ces deux classes concrètes.
L'objectif est de diminuer ce couplage en replaçant la dépendance concrète par une dépendance d'abstraction.
✅ Avec le principe du DIP
<?php
// Interface des différents exporteurs
interface Exporter
{
public function export(string $content): void;
}
// Class concrète d'export d'un PDF
class PDFExporter implements Exporter
{
public function export(string $content): void
{
// Export du contenu en PDF
}
}
// Class concrète de génération d'un rapport
class ReportGenerator
{
private $exporter;
// 🔝 Bonne pratique : Couplage faible avec une interface (= abstraction)
public function __construct(Exporter $exporter)
{
$this->exporter = $exporter;
}
public function generateReport(): void
{
$content = "Report Content";
$this->exporter->export($content);
}
}
Explications
- Cette modification réduit le couplage entre les différentes classes.
ReportGenerator
dépend maintenant de l'interfaceExporter
, et plus de la classe concrètePDFExporter
.- Il est maintenant possible d'ajouter d'autres types d'exporter sans modifier
ReportGenerator
.
Solution générique
- Utiliser des interfaces/abstractions pour éviter le couplage fort entre classes concrètes (contrat > implémentation concrète).
- Utiliser l'injection de dépendance pour fournir les implémentations concrètes des abstractions.
- Utiliser la la composition plutôt que l'héritage, et veiller à la séparation des responsabilités.
Avantages du DIP
- Réduction du couplage fort entre les implémentation et les abstractions.
- Le code est plus flexible, plus lisible et moins interdépendant.