Aller au contenu principal

Forte cohésion / Faible couplage

Notions théoriques

La forte cohésion et le faible couplage sont 2 principes fondamentaux de la programmation orientée objet. Ils guident la façon dont nous organisons nos classes pour obtenir un code plus lisible, plus maintenable et plus évolutif.

astuce

Ces 2 principes vont souvent de pair : une classe fortement cohésive est naturellement plus facile à isoler, et donc à faiblement coupler avec le reste du système.


La forte cohésion

Définition :
La forte cohésion signifie qu'une classe ne fait qu'une seule chose, et la fait bien. Toutes ses méthodes et propriétés sont directement liées à sa responsabilité principale.

C'est le principe de responsabilité unique : une classe = une responsabilité.

Mauvais exemple (faible cohésion) :

class Personnage {
private $nom;
private $pointsDeVie;

// Responsabilité 1 : gérer les données du personnage
public function getNom() { return $this->nom; }
public function setPointsDeVie($pv) { $this->pointsDeVie = $pv; }

// Responsabilité 2 : afficher des logs (ne devrait pas être ici !)
public function logAttaque($message) {
echo "[LOG] " . date('H:i:s') . " - " . $message . "\n";
}

// Responsabilité 3 : sauvegarder en base de données (ne devrait pas être ici !)
public function sauvegarder() {
// connexion à la base de données...
}
}

Bon exemple (forte cohésion) :

// Chaque classe a une seule responsabilité

class Personnage {
private $nom;
private $pointsDeVie;

public function getNom() { return $this->nom; }
public function setPointsDeVie($pv) { $this->pointsDeVie = $pv; }
}

class Logger {
public function log($message) {
echo "[LOG] " . date('H:i:s') . " - " . $message . "\n";
}
}

class PersonnageRepository {
public function sauvegarder(Personnage $personnage) {
// connexion à la base de données...
}
}
astuce

Si vous hésitez à nommer votre classe, ou si son nom contient "Et" ou "Ou", c'est souvent un signe qu'elle porte trop de responsabilités.


Le faible couplage

Définition :
Le faible couplage signifie que les classes dépendent peu les unes des autres. Une modification dans une classe n'entraîne pas une cascade de modifications dans les autres.

Le couplage représente le degré de dépendance entre les classes. Un faible couplage rend le code plus flexible et plus testable.

Mauvais exemple (fort couplage) :

class Game {
private $logger;

public function __construct() {
// Game crée elle-même son Logger : fort couplage !
$this->logger = new Logger();
}

public function startGame() {
$this->logger->log("Le jeu commence !");
}
}

Si on veut changer de Logger (par exemple utiliser un FileLogger), on doit modifier Game.

Bon exemple (faible couplage) :

class Game {
private $logger;

// Game reçoit son Logger par injection : faible couplage
public function __construct(Logger $logger) {
$this->logger = $logger;
}

public function startGame() {
$this->logger->log("Le jeu commence !");
}
}

// On peut changer de Logger sans modifier Game
$logger = new Logger();
$game = new Game($logger);
$game->startGame();
astuce

Le faible couplage s'obtient souvent grâce à l'injection de dépendances : au lieu que la classe crée elle-même ses dépendances avec new, elle les reçoit en paramètre.


Résumé des 2 principes

PrincipeQuestion à se poserObjectif
Forte cohésion"Ma classe fait-elle une seule chose ?"Chaque classe = une responsabilité
Faible couplage"Mes classes dépendent-elles trop les unes des autres ?"Limiter les dépendances entre classes

Exemple pratique

Dans notre jeu de combat, imaginons une classe Game qui gère à la fois le déroulement du combat et les logs. C'est un exemple de faible cohésion et de fort couplage.

Avant refactorisation (problématique) :

class Game {
private $fighter1;
private $fighter2;

public function __construct() {
$this->fighter1 = new Fighter("Guerrier"); // Fort couplage
$this->fighter2 = new Fighter("Mage"); // Fort couplage
}

public function startGame() {
echo "[LOG] Debut du combat\n"; // Responsabilite de log melangee
$this->fighter1->attack();
echo "[LOG] " . $this->fighter1->getNom() . " a attaque\n";
}
}

Après refactorisation (forte cohésion + faible couplage) :

class Logger {
public function log($message) {
echo "[LOG] " . $message . "\n";
}
}

class Game {
private $fighter1;
private $fighter2;
private $logger;

// Injection des dépendances : faible couplage
public function __construct(Fighter $fighter1, Fighter $fighter2, Logger $logger) {
$this->fighter1 = $fighter1;
$this->fighter2 = $fighter2;
$this->logger = $logger;
}

// Game ne gère que le combat : forte cohésion
public function startGame() {
$this->logger->log("Debut du combat !");
$this->fighter1->attack();
$this->logger->log($this->fighter1->getNom() . " a attaque");
}
}
// main.php
$fighter1 = new Fighter("Guerrier");
$fighter2 = new Fighter("Mage");
$logger = new Logger();

$game = new Game($fighter1, $fighter2, $logger);
$game->startGame();

Game ne fait plus que gérer le combat (forte cohésion), et on peut remplacer Logger sans toucher à Game (faible couplage).

Test de mémorisation/compréhension


Qu'est-ce que la forte cohésion ?


Qu'est-ce que le faible couplage ?


Quel est le signe d'une faible cohésion dans une classe ?


Comment réduire le couplage entre 2 classes ?


Pourquoi la forte cohésion et le faible couplage sont-ils liés ?


Dans notre jeu de combat, si Game crée elle-même un Logger avec new Logger(), quel problème cela pose-t-il ?



TP pour réfléchir et résoudre des problèmes

Dans ce TP, vous allez refactoriser le code de notre jeu de combat pour appliquer la forte cohésion et le faible couplage.

Vous allez :

  • Créer une classe Logger séparée avec une méthode log.
  • Modifier la classe Game pour recevoir ses dépendances par injection de dépendances.
  • Instancier les objets dans main.php et les injecter dans Game.

Étape 1 : Créer la classe Logger

Créez un fichier Logger.php contenant une classe Logger avec une méthode publique log($message) qui affiche le message sous la forme [LOG] <message>.

class Logger {
public function log($message) {
echo "[LOG] " . $message . "\n";
}
}

Étape 2 : Modifier la classe Game

Modifiez la classe Game pour :

  1. Supprimer la création interne du Logger (new Logger() dans le constructeur).
  2. Recevoir un objet Fighter et un objet Logger en paramètres du constructeur.
  3. Stocker ces objets dans des propriétés privées $fighter et $logger.
  4. La méthode startGame utilise $this->logger->log(...) pour afficher les messages.

Étape 3 : Utiliser les classes dans main.php

Dans main.php, créez séparément les objets Fighter, Logger et Game, puis injectez-les :

include 'Logger.php';
include 'Fighter.php';
include 'Game.php';

$fighter = new Fighter("Guerrier");
$logger = new Logger();
$game = new Game($fighter, $logger);
$game->startGame();

Bonne pratique - Forte cohésion et faible couplage

Une classe = une responsabilité. Si une classe gère plusieurs sujets sans rapport (données, logs, persistance), découpez-la en plusieurs classes. Puis injectez les dépendances via le constructeur plutôt que de les créer en interne avec new. Ces 2 pratiques combinées produisent un code plus facile à tester, à modifier et à faire évoluer.

Une solution