W3docs

Introduction aux Design Patterns Java

Introduction aux design patterns en Java — ce qu'ils sont et comment utiliser les plus courants.

Un design pattern est une solution réutilisable et nommée à un problème qui revient souvent lors du développement logiciel. Les patterns ne sont pas des bibliothèques à importer ni du code à copier — ce sont des structures pour agencer classes et objets que les développeurs expérimentés ont adoptées au fil du temps. Les apprendre vous donne un vocabulaire commun : dites « on va utiliser une Factory ici » et un autre développeur Java comprend immédiatement ce que vous voulez dire.

Cette page présente ce que sont les design patterns, les trois familles dans lesquelles ils se répartissent, et trois des plus courants — Strategy, Factory et Singleton — avec un exemple exécutable qui les combine. Elle suppose que vous maîtrisez les interfaces et le polymorphisme, car presque chaque pattern s'appuie sur eux.

Les patterns ont été popularisés par le livre de 1994 Design Patterns du « Gang of Four », qui en répertoriait 23. Vous n'avez pas besoin des 23 pour commencer. Une poignée, appliquée au bon moment, rend le code plus facile à modifier sans le casser.

Les trois familles

Le catalogue classique divise les patterns en trois groupes selon ce qu'ils facilitent :

FamillePréoccupationExemples
CréationnelsComment les objets sont créésSingleton, Factory, Builder
StructurelsComment les objets sont composésAdapter, Decorator, Facade
ComportementauxComment les objets interagissentStrategy, Observer, Iterator

Java lui-même est rempli de ces patterns. StringBuilder est un Builder, Iterator est le pattern Iterator, et java.util.logging utilise des Singletons. Vous utilisiez déjà des patterns sans les nommer.

Strategy : changer l'algorithme

Le pattern Strategy encapsule une famille d'algorithmes interchangeables derrière une interface commune, de sorte que le code appelant peut basculer entre eux sans changer. Définissez l'interface, écrivez chaque variante dans sa propre classe, et laissez un contexte contenir celle dont il a besoin.

interface DiscountStrategy {
    double apply(double price);
}

class PercentOff implements DiscountStrategy {
    private final double percent;
    PercentOff(double percent) { this.percent = percent; }
    public double apply(double price) { return price * (1 - percent / 100); }
}

Une classe contexte contient une DiscountStrategy et lui délègue, au lieu de brancher sur une chaîne if/switch. Ajouter une nouvelle remise signifie ajouter une classe — pas modifier le code existant. (Le contexte Checkout complet, ainsi que les variantes NoDiscount et FlatOff, apparaissent dans la démo exécutable ci-dessous.)

Factory : centraliser la création

Une Factory est une méthode unique (ou une classe) chargée de décider quel type concret instancier. Les appelants demandent un objet par description et obtiennent en retour un objet qui respecte l'interface, sans connaître la classe exacte.

static DiscountStrategy forCustomer(String tier) {
    return switch (tier) {
        case "gold"   -> new PercentOff(20);
        case "silver" -> new PercentOff(10);
        default       -> new NoDiscount();
    };
}

La logique de création se trouve en un seul endroit. Si les règles changent, vous modifiez la factory — chaque appelant continue de fonctionner sans changement.

Singleton : une seule instance

Un Singleton garantit qu'une classe n'a qu'une seule instance et fournit un point d'accès global à celle-ci. En Java moderne, un enum est le moyen le plus simple et thread-safe d'en écrire un — la JVM garantit que ses constantes sont créées exactement une fois. Consultez The Singleton Pattern pour les variantes à initialisation paresseuse et à double vérification.

enum Config {
    INSTANCE;
    private final String env = "production";
    public String env() { return env; }
}

// usage
String e = Config.INSTANCE.env();

Utilisez les Singletons avec parcimonie — ils introduisent un état global, ce qui complique les tests. Souvent, un objet unique passé via un constructeur (injection de dépendances) est le meilleur choix.

Quand ne pas utiliser un pattern

Les patterns ajoutent de la structure, et la structure a un coût. Un switch avec deux cas n'a pas besoin du pattern Strategy ; une classe instanciée une seule fois n'a pas besoin d'une Factory. Recourez à un pattern quand vous ressentez la douleur qu'il résout — branchements dupliqués, appels new éparpillés, câblage d'objets enchevêtré — pas avant. Appliquer les patterns à outrance produit un code plus difficile à lire que le problème qu'il était censé simplifier.

Strategy et Factory ensemble

L'exemple exécutable ci-dessous combine deux patterns. DiscountStrategy est l'interface Strategy avec trois implémentations ; forCustomer est une Factory qui en choisit une. Le contexte Checkout délègue à la stratégie qu'il contient, de sorte que son code ne change jamais au fur et à mesure que le comportement de tarification varie.

java— editable, runs on the server

Ce qu'il faut retenir de l'exécution :

  • Chaque niveau affiche un total différent — regular reste à 100,00 $, silver descend à 90,00 $, gold à 80,00 $, coupon à 95,00 $ — même si Checkout.total appelle la même méthode unique.
  • Le contexte Checkout ne branche jamais sur le niveau lui-même ; il délègue simplement à la DiscountStrategy qu'il contient actuellement.
  • La Factory forCustomer est le seul endroit qui sait quelle classe concrète correspond à quel niveau, de sorte que la logique de sélection se trouve en un seul endroit.
  • Changer de comportement se fait simplement avec setStrategy(...) au moment de l'exécution — ajouter une quatrième remise signifierait une nouvelle classe et un cas de plus dans la factory, sans modifier Checkout.
  • Les dernières lignes confirment la stratégie active : après avoir resélectionné gold, la politique indique 20.0% off et le total est de 80,00 $, prouvant que le contexte reflète la dernière stratégie définie.

Pratique

Pratique
Quel problème le pattern Strategy résout-il ?
Quel problème le pattern Strategy résout-il ?
Was this page helpful?