Publié le
-

La Loi de Demeter, Écrire du Code Plus Propre et Maintenable 😊

6 min lecture - 1198 mots
Auteurs
  • avatar
    Nom
    Cédric RIBALTA
    Twitter
post image

Introduction

En tant que développeur, tu as sûrement déjà entendu parler des bonnes pratiques pour écrire du code maintenable et évolutif.

Personne avec des lunettes, choquée et en train de dire quoi ?

Si ce n'est pas le cas tu peux démarrer ton initiation par un article sur les dix caractéristiques d'une fonction propre, c'est un bon premier pas.

Parmi ces pratiques, l'une des plus importantes est la Loi de Demeter, également connue sous le nom de Principe du plus faible couplage. Elle est souvent mentionnée dans des ouvrages de référence tels que le célèbre Clean Code de Robert C. Martin, alias Oncle Bob.

Mais que signifie réellement cette loi et comment peut-elle t’aider à écrire du code plus propre et plus solide ? 🤔

Qu'est-ce que la Loi de Demeter ?

La Loi de Demeter repose sur un concept simple : un objet ne devrait interagir qu'avec ses amis immédiats, c'est-à-dire les objets qu'il connaît directement. En d'autres termes, elle interdit les appels de méthodes en chaîne sur des objets qui ne sont pas directement accessibles.

Cette règle se résume par une maxime : "Ne parle qu'à tes amis directs." 👥

Concrètement, cela signifie qu'au lieu de laisser un objet interagir avec des objets internes en accédant à des sous-objets, il doit déléguer ces interactions à l'objet immédiatement responsable. Ce principe permet de découpler les classes entre elles, facilitant ainsi la maintenance du code. 🔧

Tu peux aussi comnbiner la Loi de Demeter avec le principe d'inversion de dépendances pour obtenir un code vraiment plus robuste.

Pourquoi c’est important ?

Appliquer la Loi de Demeter dans ton code permet de :

  • Réduire le couplage : Moins de dépendances entre les classes, ce qui rend ton code plus robuste face aux changements. 💪
  • Améliorer la lisibilité : Éviter les appels de méthodes en cascade rend le code plus facile à lire et à comprendre. 👓
  • Faciliter les tests unitaires : Des objets faiblement couplés sont plus faciles à tester, car chaque objet se concentre uniquement sur ses propres responsabilités (et c'est encore mieux si chaque fonction fait une seule chose). ✅

Détail des avantages de la Loi de Demeter

  • Réduction du couplage : Le principal avantage de la Loi de Demeter est la réduction du couplage entre les objets. En limitant les interactions à celles qui sont strictement nécessaires, ton code devient plus modulaire et indépendant. Cela facilite les modifications ou ajouts futurs sans risque de casser des fonctionnalités existantes (à combiner avec le principe d'ouverture/fermeture). Imaginons que tu modifies la structure interne d’un objet comme Engine, si cette structure n'est connue que de l'objet lui-même, les changements n'affecteront pas directement les autres objets comme Car.

  • Robustesse face aux changements : Plus ton code est faiblement couplé, moins il sera affecté par des changements dans d'autres parties du système. Un code fortement couplé, en revanche, tend à propager les effets des changements à travers de nombreux objets, ce qui rend les modifications risquées et coûteuses en termes de temps (de manière générale, il faut faire attention aux effets secondaires). 🕒

  • Clarté du code : Quand le code est écrit en respectant la Loi de Demeter, il est plus facile à lire. Les chaînes d'appels complexes, souvent sources de confusion, sont évitées. Cela rend ton code non seulement plus facile à maintenir mais aussi plus accessible pour les nouveaux développeurs qui rejoindraient ton projet. ✨

Exemple de violation de la Loi de Demeter

Prenons un exemple en TypeScript pour illustrer une violation courante de la Loi de Demeter.

class FuelTank {
  getFuelLevel(): number {
    return 80 // Niveau de carburant
  }
}

class Engine {
  fuelTank: FuelTank

  constructor(fuelTank: FuelTank) {
    this.fuelTank = fuelTank
  }

  getFuelTank(): FuelTank {
    return this.fuelTank
  }
}

class Car {
  engine: Engine

  constructor(engine: Engine) {
    this.engine = engine
  }

  getEngine(): Engine {
    return this.engine
  }
}

const myCar = new Car(new Engine(new FuelTank()))
console.log(myCar.getEngine().getFuelTank().getFuelLevel()) // Mauvais !

Ici, l’objet Car accède directement au réservoir de carburant (FuelTank) via une série d’appels de méthodes. C'est une violation de la Loi de Demeter, car Car ne devrait pas connaître les détails internes de Engine ou de FuelTank.

Respect de la Loi de Demeter

Pour respecter la Loi de Demeter, il est essentiel d’éviter ces chaînes d'appels. Une solution consiste à ajouter une méthode dans la classe Car qui encapsule cette logique :

class Car {
  engine: Engine

  constructor(engine: Engine) {
    this.engine = engine
  }

  getFuelLevel(): number {
    return this.engine.getFuelTank().getFuelLevel()
  }
}

const myCar = new Car(new Engine(new FuelTank()))
console.log(myCar.getFuelLevel()) // Correct

Dans cette version, Car expose une méthode getFuelLevel() qui masque les détails internes de Engine et FuelTank. Le couplage est réduit et Car ne dépend plus de la structure de FuelTank.

Impact sur les tests unitaires

L’application de la Loi de Demeter améliore également la testabilité du code. Pourquoi ? Parce que le code faiblement couplé est plus facile à isoler lors des tests unitaires (qui devraient représenter 70% de ta pyramide de tests). 🔍

  • Test des objets en isolation : Un objet qui respecte la Loi de Demeter ne dépend que de ses relations immédiates, ce qui facilite la création de mocks ou de fakes lors des tests unitaires. Si un objet Car n’a besoin de connaître que son moteur (Engine), alors le test unitaire peut se concentrer uniquement sur cette relation directe, sans se soucier des autres détails internes comme le réservoir (FuelTank).

  • Réduction des tests fragiles : Les chaînes d'appels peuvent rendre les tests fragiles, car un changement dans une classe interne peut casser plusieurs tests à la fois. En respectant la Loi de Demeter, tu minimises ces risques, car chaque classe gère ses propres responsabilités, et les changements internes restent confinés à la classe concernée. 🚦

Liens avec d'autres principes SOLID

La Loi de Demeter est souvent comparée ou liée à d'autres principes de conception du code, notamment le Single Responsibility Principle (SRP), qui stipule qu'une classe ne doit avoir qu'une seule responsabilité. Respecter la Loi de Demeter contribue à cette idée en s’assurant que chaque classe gère uniquement ce qui lui est propre.

  • Single Responsibility Principle (SRP) : En découplant les responsabilités des classes, tu simplifies leur gestion et tu garantis qu'elles ne font qu'une seule chose. Par exemple, si Car ne connaît que le moteur (Engine) et n'interagit pas directement avec le réservoir (FuelTank), cela renforce le principe SRP. 🛠️

  • Interface Segregation Principle (ISP) : La Loi de Demeter encourage également à séparer les interfaces et à minimiser les interactions avec des objets qui ne devraient pas être en contact direct. Cela rejoint l'idée que les interfaces doivent être réduites au strict nécessaire pour éviter les dépendances inutiles.

Conclusion

Appliquer la Loi de Demeter dans ton code permet de réduire le couplage, d'améliorer la lisibilité et de simplifier les tests unitaires. Bien qu’il puisse être tentant de chaîner des appels de méthodes pour accéder à des objets internes, cela crée un couplage excessif qui rend le code fragile face aux modifications futures.

La prochaine fois que tu vois des chaînes d'appels dans ton code, demande-toi : Suis-je en train de violer la Loi de Demeter ? 🤨 En encapsulant les interactions et en réduisant la dépendance entre les objets, tu rendras ton code plus robuste et évolutif. 🚀

Car comme dirait notre ami Judge Dredd :

Judge Dredd disant je suis la loi