🏛️ Cours : Les Patrons de Conception (Design Patterns) – L’Intégrale

5.1. Introduction

Les Patrons de Conception sont des solutions éprouvées à des problèmes récurrents. Ils ne sont pas du code, mais des modèles à adapter.

Le saviez-vous ? Ils ont été popularisés par le livre “Design Patterns” (1994) écrit par le “Gang of Four” (GoF) : Erich Gamma, Richard Helm, Ralph Johnson et John Vlissides.

Pourquoi les utiliser ?

  1. Vocabulaire commun : Dire “J’utilise un Observer ici” est plus rapide que d’expliquer 10 lignes de logique.

  2. Best Practices : Ils respectent naturellement les principes SOLID (surtout OCP et DIP).

  3. Maintenabilité : Le code est découpé de manière standardisée.


5.2. Les Patrons de Création (Creational)

Comment créer des objets sans coupler le code à leurs classes concrètes.

5.2.1. 🏭 Factory Method (La Fabrique)

“Déléguer la création aux sous-classes.”

  • Le Problème : Faire new Chevalier() lie votre code à la classe Chevalier. Si vous voulez un Archer, il faut tout changer.

  • La Solution : Une interface UnitFactory avec une méthode createUnit().

  • Lien SOLID : DIP (On dépend de l’interface) & OCP (On ajoute des usines sans toucher au code client).

Extrait de code

@startuml
!theme sunlust
interface Unit {
  + attack()
}
class Chevalier {
  + attack()
}
class Archer {
  + attack()
}

abstract class UnitFactory {
  + createUnit(): Unit {abstract}
}
class ChevalierFactory {
  + createUnit(): Unit
}
class ArcherFactory {
  + createUnit(): Unit
}

Unit <|.. Chevalier
Unit <|.. Archer
UnitFactory <|-- ChevalierFactory
UnitFactory <|-- ArcherFactory
ChevalierFactory ..> Chevalier : creates
ArcherFactory ..> Archer : creates
@enduml

5.2.2. 🏗️ Builder (Le Monteur)

“Construire des objets complexes étape par étape.”

  • Le Problème : Un constructeur avec 10 paramètres (new Hero(100, 50, "Epée", true, null, ...)). C’est illisible (Télescopic Constructor Anti-pattern).

  • La Solution : Un objet Builder qui configure l’objet final pas à pas.

@startuml
!theme sunlust
class HeroBuilder {
  - hero: Hero
  + setHP(int)
  + setWeapon(String)
  + setArmor(String)
  + build(): Hero
}
class Hero
HeroBuilder ..> Hero : creates
note right: Hero h = new HeroBuilder()\n .setHP(100)\n .setWeapon("Sword")\n .build();
@enduml

5.2.3. 👑 Singleton (L’Unique)

“Garantir qu’une classe n’a qu’une seule instance.”

  • Usage : GameManager, DatabaseConnection, Logger.

  • Attention : C’est souvent considéré comme un anti-pattern s’il est mal utilisé (il introduit un état global et cache les dépendances).

public class GameManager {
    private static GameManager instance;
    private GameManager() {} // Constructeur privé !

    public static GameManager getInstance() {
        if (instance == null) instance = new GameManager();
        return instance;
    }
}

📋 Autres Patrons de Création (Résumé)

Patron Description Rapide
Abstract Factory Une “Super-Usine” qui crée des familles d’objets liés (ex: ElfFactory crée ElfArcher, ElfMage, ElfCastle).
Prototype Créer un nouvel objet en clonant un objet existant (utile si la création est coûteuse).

5.3. Les Patrons de Structure (Structural)

Comment assembler des objets et des classes pour former des structures plus grandes.

5.3.1. 🔌 Adapter (L’Adaptateur)

“Faire collaborer des objets incompatibles.”

  • Le Problème : Vous avez un système qui attend ILogger.log(msg), mais vous voulez utiliser une librairie tierce qui a ThirdPartyLog.writeToDisk(msg).

  • La Solution : Une classe Adapter qui enveloppe l’objet incompatible.

5.3.2. 🎁 Decorator (Le Décorateur)

“Ajouter des responsabilités dynamiquement.”

  • Le Problème : Vous avez une classe Epee. Vous voulez une EpeeDeFeu, une EpeeDeGlace, une EpeeDeFeuEtGlace. L’héritage explose (EpeeDeFeu extends Epee).

  • La Solution : Envelopper l’objet. new Feu(new Glace(new Epee())).

  • Lien SOLID : OCP (On ajoute des comportements sans modifier la classe de base).

@startuml
!theme sunlust
interface Arme {
  + getDegats(): int
}
class EpeeBasique {
  + getDegats(): return 10
}
abstract class Decorator implements Arme {
  protected arme: Arme
}
class BonusFeu extends Decorator {
  + getDegats(): return arme.getDegats() + 5
}

Arme <|.. EpeeBasique
Decorator o--> Arme
@enduml

5.3.3. 🎭 Facade (La Façade)

“Simplifier une interface complexe.”

  • Le Problème : Pour démarrer le jeu, il faut : SoundSystem.init(), Gfx.load(), Network.connect(). Le code client devient lourd.

  • La Solution : Une classe GameEngine avec une seule méthode start().

5.3.4. 🌳 Composite

“Traiter un groupe d’objets comme un seul objet.”

  • Usage : Un système de fichiers (Dossier contient Fichiers et Dossiers), ou un Inventaire (Sac contient Objets et Sacs).

📋 Autres Patrons de Structure (Résumé)

Patron Description Rapide
Bridge Séparer une abstraction de son implémentation pour qu’elles varient indépendamment (ex: RemoteControl et Device).
Proxy Un substitut pour un autre objet (ex: charger une image lourde seulement quand on l’affiche).
Flyweight Partager l’état commun pour supporter un grand nombre d’objets (ex: afficher 1000 arbres identiques dans un jeu).

5.4. Les Patrons de Comportement (Behavioral)

Comment les objets communiquent et se répartissent les responsabilités.

5.4.1. 🧠 Strategy (La Stratégie)

“Interchanger des algorithmes à la volée.”

  • Le Problème : if (mode == "AGRESSIF") { ... } else if (mode == "DEFENSIF") { ... }.

  • La Solution : unit.setComportement(new ComportementAgressif()).

@startuml
!theme sunlust
class Context {
  - strategy: Strategy
  + executeStrategy()
}
interface Strategy {
  + execute()
}
class ConcreteStrategyA
class ConcreteStrategyB

Context o-> Strategy
Strategy <|.. ConcreteStrategyA
Strategy <|.. ConcreteStrategyB
@enduml

5.4.2. 📡 Observer (L’Observateur)

“Notifier plusieurs objets d’un changement d’état.”

  • Le Problème : Quand le Héros perd des PV, il faut mettre à jour : la barre de vie, le son (cri), l’écran (rouge). Le Héros ne doit pas connaître ces classes UI.

  • La Solution : Le Héros est un “Sujet”. L’UI est un “Observateur”. Quand PV change, le Héros crie “Notifiez-vous !”.

@startuml
!theme sunlust
class Sujet {
  - observers: List
<Observer>
  + attach(o: Observer)
  + notify()
}
interface Observer {
  + update()
}
class BarreDeVieUI {
  + update()
}
class EcranRougeFX {
  + update()
}

Sujet o--> Observer
Observer <|.. BarreDeVieUI
Observer <|.. EcranRougeFX
@enduml

5.4.3. 🚦 State (L’État)

“Modifier le comportement d’un objet quand son état change.”

  • Le Problème : Un monstre se comporte différemment s’il est CALME, ENERVE ou BLESSE. Utiliser des switch case devient vite un enfer.

  • La Solution : Créer des classes EtatCalme, EtatEnerve. Le monstre délègue l’action à son état actuel.

📋 Autres Patrons de Comportement (Résumé)

Patron Description Rapide
Command Encapsuler une demande sous forme d’objet (ex: Ctrl+Z, file d’attente d’actions).
Iterator Parcourir une collection sans connaître sa structure interne (foreach).
Template Method Définir le squelette d’un algo dans une classe mère, laisser les filles redéfinir certaines étapes.
Chain of Responsibility Passer une demande le long d’une chaîne d’objets (ex: gestionnaire d’événements, support technique).
Mediator Un objet central qui gère les interactions complexes entre objets (tour de contrôle).
Memento Capturer et restaurer l’état interne d’un objet (Système de Sauvegarde).
Visitor Séparer un algorithme de la structure d’objet sur laquelle il opère (Ajouter une opération sans changer les classes).
Interpreter Pour évaluer un langage ou une grammaire.

5.5. Synthèse : Quel Patron pour mon Projet ?

Pour votre Projet Fil Rouge, voici les plus pertinents :

Si vous avez besoin de… Utilisez…
Créer des monstres ou objets variés Factory Method
Configurer un personnage complexe Builder
Gérer le moteur de jeu ou la DB Singleton (avec modération)
Avoir des armes avec bonus (Feu, Glace) Decorator
Gérer l’inventaire (Sacs dans Sacs) Composite
Changer l’IA des ennemis (Attaque/Fuite) Strategy
Mettre à jour l’interface (PV, Mana) Observer
Gérer les actions du joueur (Undo/Redo) Command