Mon blog

Les Design Patterns en Java

Les design patterns (ou patrons de conception) sont des solutions générales réutilisables pour les problèmes fréquemment rencontrés dans la conception de logiciels. Ils fournissent des modèles éprouvés pour structurer le code de manière robuste, maintenable et évolutive. En Java, l’utilisation de ces patterns est particulièrement précieuse pour développer des applications fiables. Voici quelques exemples courants de design patterns en Java :

1. Singleton

Le design pattern Singleton garantit qu’une classe n’a qu’une seule instance tout en fournissant un point d’accès global à cette instance. Ce pattern est utile pour les objets qui doivent être uniques au sein d’une application, tels que les gestionnaires de bases de données, les gestionnaires de configurations, ou les journaux de log.

public class Singleton {
private static Singleton instance;

private Singleton() {}

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

L’utilisation du pattern Singleton peut aider à contrôler l’accès à une ressource partagée, mais il doit être utilisé avec précaution pour éviter les problèmes liés aux dépendances globales et aux conditions de concurrence.

2. Factory Method

Le design pattern Factory Method est un pattern de création qui définit une interface pour créer des objets dans une classe mère, tout en permettant aux sous-classes de modifier le type d’objets créés. Ce pattern est particulièrement utile lorsque le type exact de l’objet à créer n’est pas connu à l’avance.

// Interface produit
public interface Product {
void use();
}

// Classes concrètes de produit
public class ConcreteProductA implements Product {
@Override
public void use() {
System.out.println("Using ConcreteProductA");
}
}

public class ConcreteProductB implements Product {
@Override
public void use() {
System.out.println("Using ConcreteProductB");
}
}

// Classe créateur
public abstract class Creator {
public abstract Product factoryMethod();
public void someOperation() {
Product product = factoryMethod();
product.use();
}
}

// Classes concrètes de créateur
public class ConcreteCreatorA extends Creator {
@Override
public Product factoryMethod() {
return new ConcreteProductA();
}
}

public class ConcreteCreatorB extends Creator {
@Override
public Product factoryMethod() {
return new ConcreteProductB();
}
}

// Utilisation
public class FactoryMethodPatternDemo {
public static void main(String[] args) {
Creator creatorA = new ConcreteCreatorA();
creatorA.someOperation(); // Output: Using ConcreteProductA
Creator creatorB = new ConcreteCreatorB();
creatorB.someOperation(); // Output: Using ConcreteProductB
}
}

Le pattern Factory Method permet de créer des objets sans spécifier la classe exacte de l’objet qui sera créé, favorisant ainsi une plus grande flexibilité et extensibilité du code.

3. Observer

Le design pattern Observer est un pattern comportemental qui définit une relation de dépendance un-à-plusieurs entre des objets, de sorte que lorsque l’état d’un objet change, tous ses dépendants en sont notifiés et mis à jour automatiquement. Ce pattern est également connu sous le nom de Publish-Subscribe.

import java.util.ArrayList;
import java.util.List;

// Interface observer
public interface Observer {
  void update(String message);
}

// Interface sujet
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}

// Sujet concret
public class ConcreteSubject implements Subject {
private List<Observer> observers = new ArrayList<>();
private String state;

@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}

@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}

@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(state);
}
}

public void setState(String state) {
this.state = state;
notifyObservers();
}
}

// Observateurs concrets
public class ConcreteObserverA implements Observer {
@Override
public void update(String message) {
System.out.println("ConcreteObserverA received: " + message);
}
}

public class ConcreteObserverB implements Observer {
@Override
public void update(String message) {
System.out.println("ConcreteObserverB received: " + message);
}
}

// Utilisation
public class ObserverPatternDemo {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();

Observer observerA = new ConcreteObserverA();
Observer observerB = new ConcreteObserverB();

subject.registerObserver(observerA);
subject.registerObserver(observerB);

subject.setState("State 1"); // Output: ConcreteObserverA received: State 1
// ConcreteObserverB received: State 1
subject.setState("State 2"); // Output: ConcreteObserverA received: State 2
// ConcreteObserverB received: State 2
}
}

Le pattern Observer est idéal pour gérer les notifications et les dépendances dynamiques, ce qui le rend très utile dans les interfaces utilisateur, les systèmes de notification, et les applications réactives.

4. Strategy

Le design pattern Strategy est un pattern comportemental qui permet de définir une famille d’algorithmes, de les encapsuler dans des classes séparées et de les rendre interchangeables. Cela permet à l’algorithme de varier indépendamment du client qui l’utilise.

// Interface stratégie
public interface Strategy {
int doOperation(int num1, int num2);
}

// Stratégies concrètes
public class Addition implements Strategy {
  @Override
  public int doOperation(int num1, int num2) {
    return num1 + num2;
  }
}

public class Subtraction implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}

public class Multiplication implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 * num2;
}
}

// Contexte
public class Context {
private Strategy strategy;

public Context(Strategy strategy) {
this.strategy = strategy;
}

public int executeStrategy(int num1, int num2) {
return strategy.doOperation(num1, num2);
}
}

// Utilisation
public class StrategyPatternDemo {
public static void main(String[] args) {
Context context = new Context(new Addition());
System.out.println("10 + 5 = " + context.executeStrategy(10, 5));

context = new Context(new Subtraction());
System.out.println("10 - 5 = " + context.executeStrategy(10, 5));

context = new Context(new Multiplication());
System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
}
}

Le pattern Strategy permet de créer des systèmes flexibles et modulaires, facilitant ainsi la modification ou l’ajout de comportements sans perturber le code existant.

Conclusion

Ces exemples illustrent comment les design patterns peuvent être implémentés en Java pour résoudre différents types de problèmes de conception logicielle. Chaque pattern offre une solution éprouvée pour structurer votre code de manière plus efficace et maintenable, contribuant ainsi à la création de logiciels robustes et évolutifs. En adoptant ces patterns, les développeurs peuvent améliorer la qualité de leurs applications et faciliter leur maintenance à long terme.

© 2016 | Powred By Ayoub Ben Khiroun