Contexte et enjeux
Les design patterns sont des solutions établies à des problèmes récurrents dans la conception logicielle. Ils permettent aux développeurs de créer des systèmes plus modulaires, flexibles et faciles à maintenir. Dans un contexte d'évolution rapide du secteur technologique, comprendre et appliquer les design patterns essentiels est crucial pour développer des solutions robustes et évolutives.
Concepts clés
1. Singleton
Le singleton garantit qu'une classe a une seule instance tout au long de l'exécution d'un programme. C'est utile pour gérer des ressources comme les connexions à une base de données ou le stockage global des paramètres de configuration.
public class DatabaseConnection {
private static DatabaseConnection instance;
// Private constructor to prevent instantiation outside the class
private DatabaseConnection() {}
public static synchronized DatabaseConnection getInstance() {
if (instance == null) {
instance = new DatabaseConnection();
}
return instance;
}
public void connect() {
System.out.println("Connecting to database...");
}
}
2. Factory Method
La factory method permet de déléguer la création d'objets à des classes dérivées, plutôt que directement au constructeur. Cela facilite le changement et l'évolutivité du code.
public interface Shape {
void draw();
}
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Drawing a circle");
}
}
public class Factory {
public static Shape getShape(String shapeType) {
if (shapeType == null) {
return null;
}
if (shapeType.equalsIgnoreCase("CIRCLE")) {
return new Circle();
}
// Add more shapes here
return null;
}
}
3. Observer
L'observer permet de définir un mécanisme dépendance entre des objets afin que, lorsqu'un objet change d'état, tous les autres soient notifiés et mis à jour automatiquement.
import java.util.ArrayList;
import java.util.List;
public interface Observer {
void update(String message);
}
public class Subject {
private List<Observer> observers = new ArrayList<>();
private String state;
public void attach(Observer observer) {
observers.add(observer);
}
public void detach(Observer observer) {
observers.remove(observer);
}
public void setState(String state) {
this.state = state;
notifyObservers();
}
private void notifyObservers() {
for (Observer observer : observers) {
observer.update(state);
}
}
}
4. Adapter
L'adapter permet de convertir l'interface d'un objet en une interface que les clients attendent. Cela facilite la réutilisation des classes existantes sans modifier leur code.
public class AudioPlayer {
public void play(String audioType, String fileName) {
if (audioType.equalsIgnoreCase("MP3")) {
System.out.println("Playing mp3 file");
} else if (audioType.equalsIgnoreCase("WAV")) {
System.out.println("Playing wav file");
} else {
System.out.println("Unsupported format");
}
}
}
public interface MediaPlayer {
void play(String audioType, String fileName);
}
public class Mp4Player implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
// Not implemented
}
@Override
public void playMp4(String fileName) {
System.out.println("Playing mp4 file");
}
}
public class MediaAdapter implements MediaPlayer {
private AdvancedMediaPlayer advancedMusicPlayer;
public MediaAdapter(AdvancedMediaPlayer advancedMusicPlayer) {
this.advancedMusicPlayer = advancedMusicPlayer;
}
@Override
public void play(String audioType, String fileName) {
if (audioType.equalsIgnoreCase("MP4")) {
advancedMusicPlayer.playMp4(fileName);
}
// Add more media types here
}
}
5. Strategy
La stratégie permet de définir une famille d'algorithmes, encapsuler chaque un, et les rendre interchangeables. Cela permet au client de changer l'algorithme utilisé à la volée.
public interface PaymentStrategy {
void pay(int amount);
}
public class CreditCardPayment implements PaymentStrategy {
private String cardNumber;
private String name;
public CreditCardPayment(String cardNumber, String name) {
this.cardNumber = cardNumber;
this.name = name;
}
@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " using credit card");
}
}
public class ShoppingCart {
private PaymentStrategy paymentStrategy;
public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
public void checkout(int totalAmount) {
paymentStrategy.pay(totalAmount);
}
}
Guide pratique pas à pas
Étape 1 : Identifier le problème
Avant de chercher un design pattern, identifiez clairement le problème que vous essayez de résoudre dans votre code.
Étape 2 : Recherchez les patterns appropriés
Utilisez des ressources en ligne comme Refactoring.Guru ou des livres comme "Design Patterns: Elements of Reusable Object-Oriented Software" pour identifier le pattern qui correspond à votre problème.
Étape 3 : Adapter le pattern au contexte
Adaptez le design pattern au contexte de votre application. Cela peut impliquer la modification d'interfaces, la création de nouvelles classes ou l'utilisation d'injection de dépendances.
Étape 4 : Implémentez et testez
Implémentez le pattern dans votre code et assurez-vous qu'il fonctionne comme prévu. Effectuez des tests unitaires pour vérifier la qualité du code.
Comparatif ou tableau recapitulatif
| Design Pattern | Utilisation |
|---|---|
| Singleton | Gestion de ressources uniques |
| Factory Method | Création d'objets dynamiquement |
| Observer | Communication entre objets en temps réel |
| Adapter | Réutilisation de classes existantes |
| Strategy | Sélection dynamique d'algorithme |
Retour d'expérience concret
En tant que développeur expérimenté, j'ai souvent rencontré des problèmes similaires et j'ai utilisé les design patterns essentiels pour résoudre ces problèmes. Par exemple, dans un projet de gestion de base de données, j'ai utilisé le pattern Singleton pour gérer la connexion à la base de données afin d'éviter une instanciation multiple et potentiellement coûteuse.
Checklist ou plan d'action
- Identifier clairement le problème
- Rechercher les patterns appropriés
- Adapter le pattern au contexte de votre application
- Implémenter et tester le pattern
- Réviser et optimiser le code après l'implémentation