T: / Corrigés des challenges / PHP
Le S de SOLID implique que chaque classe ait un seul rôle. On se base sur ce principe pour refacto du code.
Dans un précédent corrigé, pour le challenge Wall-E, j’ai créé une seule classe pour résoudre le challenge.
La classe Robot permettait de tout gérer :
Plusieurs méthodes de cette classe ont été créées en « public » alors qu’elles pourraient être « private » car il n’y a que la classe qui les utilise. C’est en général un bon indicateur qui révèle qu’on ne respecte pas le « S » de « SOLID », à savoir le principe de « SINGLE RESPONSABILITY« . Ce principe implique qu’une classe doit avoir une seule responsabilité. Notre classe Robot gère donc trop de choses.
On va essayer de refactoriser tout ça. On va en profiter pour tout (re)coder en anglais. Pour bien comprendre ce corrigé, il est important de découvrir la première version, qui détaille la logique du challenge et les explications du code.
En réfléchissant un peu, on peut découper notre unique classe Robot en plusieurs :
La classe Robot va donc évoluer :
class Robot
{
private int $speed;
private Battery $battery;
private Lifter $lifter;
public function __construct(int $force, int $speed, int $batteryLevel)
{
$this->speed = $speed;
$this->lifter = new Lifter($force);
$this->battery = new Battery($batteryLevel);
}
}
Classe Battery :
class Battery
{
private const LEVEL_FOR_RECHARGE = 20;
private const MAX = 100;
private const MIN = 0;
public function __construct(
private int $level
)
{
}
public function recharge(int $speed): void
{
// Si la batterie de Wall-E passe sous les 20%, il doit aller se recharger
if ($this->level < self::LEVEL_FOR_RECHARGE) {
// Mais si la vitesse de Wall-E est supérieure à la batterie restante, alors il tombe en panne et le petit robot s'arrête.
if ($this->level - $speed <= self::MIN) {
$this->level = self::MIN;
return;
}
// Il se recharge à 100% et utilise à nouveau de la batterie pour revenir, le même montant (vitesse)
$this->level = self::MAX - $speed;
}
}
public function consume(int $battery): void
{
$this->level -= $battery;
}
public function getLevel(): int
{
return $this->level;
}
public function isDown(): bool
{
return $this->level <= self::MIN;
}
}
Classe Lifter :
class Lifter
{
private const BATTERY_RATIO_TO_LIFT_WEIGHT = 2;
private const BATTERY_IF_NOT_POSSIBLE_TO_LIFT_WEIGHT = 2;
private const BATTERY_IF_OK_TO_LIFT_WEIGHT = 1;
public function __construct(
private int $force
)
{
}
public function getConsumedBattery(int $weight, int $batteryCurrentLevel): int
{
// Force supérieure ou égale au poids du déchet => 1% de batterie
if ($this->force >= $weight) {
return self::BATTERY_IF_OK_TO_LIFT_WEIGHT;
}
// Sinon, la différence * 2
$battery = ($weight - $this->force) * self::BATTERY_RATIO_TO_LIFT_WEIGHT;
// Je ne dois pas dépasser la moitié de la batterie
if ($battery > ($batteryCurrentLevel / 2)) {
return self::BATTERY_IF_NOT_POSSIBLE_TO_LIFT_WEIGHT;
}
return $battery;
}
}
Et pour finir la classe Robot et la méthode principale de traitement de déchets :
/**
* @param int[] $trash
*/
public function handleTrash(array $trash): void
{
foreach ($trash as $trashWeight) {
if ($this->battery->isDown()) {
break;
}
$this->battery->consume(
$this->lifter->getConsumedBattery($trashWeight, $this->battery->getLevel())
);
$this->battery->recharge($this->speed);
}
}
Ce qui devient très intéressant en séparant correctement la logique dans différentes classes, c’est que je vais pouvoir aussi répartir mes tests en plusieurs fichiers : (lien vers le code à la fin)
Pour bien comprendre les différents tests unitaires, la notion de dataprovider, n’hésite pas à te référer à la première version du corrigé qui explique tout ça en détails : Tests unitaires en PHP #3 : TDD avec PHPUnit et des dataprovider.
On a vu ici le S de SOLID et ce principe qui consiste à faire en sorte qu’une classe n’ait qu’une seule responsabilité. On aurait pu pousser encore plus loin en créant une classe Trash dédiée à chaque déchet. Cette classe aurait eu une propriété weight et un getteur getWeight notamment.
Les points clés de ce premier principe SOLID :
Il reste OLID à creuser maintenant 😉
Voici 2 ressources très intéressantes permettant d’aller plus loin :
N’hésite pas à mettre en pratique ces principes avec d’autres challenges de programmation !
Le code complet de ce corrigé est disponible sur Github.
Other content to discover