La loi de Demeter est une règle de conception souvent décrite de la manière suivante : "Ne parlez qu'à vos amis immédiats".
Prenons comme exemple la class Car
suivante :
class Car
{
public function __construct(
protected Engine $engine,
) {}
public function isRunning(): bool
{
return $this->engine->state->isRunning();
}
}
La méthode isRunning
est une violation de la loi de Demeter car elle manipule une instance de State
provenant d'une de ses instances de class : Engine
.
Dit de façon plus triviale : «IsRunning
est dépendant d'une dépendance de ses dépendances».
Ce couplage est délétère pour la composition de vos class et affectera de manière négative son afferent et son efferent coupling.
Car
étant directement dépendant de la class State
, un changement ou une refactorisation de cette dernière impactera à la fois Engine
et Car
, alors que seul Engine
devrait être directement concerné, la loi de Demeter est souvent synonyme d'un probleme de conception.
Voyons désormais comment refactoriser ce code pour respecter la loi de Demeter.
class Car
{
public function __construct(
protected Engine $engine,
) {}
public function isRunning(): bool
{
return $this->engine->isRunning();
}
}
class Engine
{
public function __construct(
protected State $state,
) {}
public function isRunning(): bool
{
return $this->state->isRunning();
}
}
class State
{
public function isRunning(): bool
{
return true;
}
}
En respectant cette nouvelle architecture, chaque class est parfaitement découplée et ne parle qu'à ses amis immédiats, il n'y a pas de dépendance superflue.
Il est désormais plus facile de faire évoluer la class Car
, cette dernière n'étant plus dépendante de la structure, et de l'état, de ses dépendances.
Tester cette class Car
est également plus aisé qu'auparavant car seule la class Engine
nécessitera d'une doublure.