Cet article est exceptionnellement un peu théorique, mais c’est pour la bonne cause.
Je souhaite aujourd’hui vous parler de la loi de Déméter.
Il s’agit d’une loi essentielle que tout développeur doit connaitre.
A la fin de cet article, vous serez capable d’adapter votre code pour utiliser cette loi et d’améliorer la qualité globale de votre application.
Si vous souhaitez connaitre d’autres principes théoriques (mais importants), je vous invite à lire la série d’articles sur les principes SOLID.
D’où vient le problème ?
Généralement, quand on développe une classe, on ne fait pas toujours attention aux dépendances de celle-ci.
Une classe peut utiliser plusieurs autres classes soit par composition, soit par des passages par argument dans les méthodes.
Résultat : quand vous écrivez une classe, elle va générer des dépendances vers d’autres classes et parfois de manière transitoire, c’est-à-dire :
- une classe A utilise une classe B
- une méthode de B retourne un type C
- la classe A va donc pouvoir utiliser le type C
Jusque là, vous allez me dire que c’est du classique. Pourtant, il est possible de faire des choix de conception justement dans ce cas précis.
Loi de Déméter
La loi de Déméter peut s’énoncer de la manière suivante :
Chaque unité doit avoir une connaissance limitée des autres unités : elle doit voir que les unités étroitement liées à l’unité actuelle.
Traduit autrement, nous pourrions dire : « Chaque unité ne doit parler qu’à ses amis. Ne pas parler aux étrangers« .
En pratique, cela veut dire qu’une méthode d’une classe peut invoquer les méthodes :
- de l’objet lui-même
- des arguments de la méthode
- des objets crées dans la méthode
- des propriétés et champs de l’objet
En suivant la loi de Déméter, votre code sera plus facile à maintenir et donc plus facile à adapter.
Par conséquent, vos classes seront moins dépendantes de la structure interne des autres classes.
Exemple pratique de la Loi de Déméter
Pour finir, je vous propose un exemple pratique pour illustrer la Loi de Déméter.
L’exemple est celui qui est souvent utilisé dans la littérature : le Paperboy (livreur de journal), le client et le porte-monnaie.
{
decimal fundsCollected;
public Paperboy()
{
Customers = new List();
}
public List Customers { get; set; }
}
public class Customer
{
public Customer() : this(null) { }
public Customer(Wallet wallet)
{
Wallet = wallet;
}
public Wallet Wallet { get; set; }
}
public class Wallet
{
public Wallet(decimal money)
{
Money = money;
}
public decimal Money { get; set; }
}
[/csharp]
Imaginons que nous devions écrire une méthode pour demander le paiement du journal à l’ensemble des clients.
On pourrait alors écrire cette méthode en C# :
{
// Le journal coûte 1€
decimal payment = 1m;
foreach (Customer customer in Customers)
{
if (customer.Wallet.Money >= payment)
{
customer.Wallet.Money -= payment;
fundsCollected += payment;
}
}
}
[/csharp]
Ce code fonctionne bien mais il pose un problème : la méthode CollectPayments a une connaissance approfondie de la classe Wallet (car elle utilise Money).
En relisant la Loi de Déméter, on se rend compte que le problème doit être corrigé à cause de la dépendance forte.
En cas de changement de la classe Wallet (si on renomme Money par exemple), cela aurait un impact sur la méthode CollectPayments (ce qui n’est pas bon).
On pourrait donc modifier cette méthode pour écrire ceci :
[csharp] // Ajouter cette méthode dans la classe Customer// Elle s’occupe de gérer le paiement
public decimal MakePayment(decimal amount)
{
if (Wallet.Money >= amount)
{
Wallet.Money -= amount;
return amount;
}
return 0m;
}
// Nouvelle version de CollectPayments (dans PaperBoy)
public void CollectPayments()
{
decimal charge = 1m;
foreach (Customer customer in Customers)
{
decimal payment = customer.MakePayment(charge);
if (payment != 0m)
{
fundsCollected += payment;
}
}
}
[/csharp]
La loi de Déméter est intéressante pour plusieurs raisons :
- Tout d’abord, elle est facile à comprendre et facile à mettre en oeuvre
- Elle permet de mieux séparer les comportements (chaque méthode réalise une seule chose, cela rejoint le principe SRP)
- Elle permet de faciliter la maintenance puisque Paperboy devient indépendant aux modifications de Wallet (ce qui n’était pas le cas avant).
Bref, vous l’aurez compris : je vous conseille fortement de la mettre en place. Cela va grandement simplifier votre code.
Et comme à chaque fois : ne modifiez pas tout votre code existant. Faites des modifications par petit morceau.
Commencez, par exemple, par appliquer la loi de Déméter sur votre nouveau code.
PS : Pour en savoir plus sur Déméter, la déesse grecque, rendez-vous sur Wikipédia : Déméter.