Modèles de conception en C++ avec exemples : de A à Z
Les modèles de conception (design patterns) sont des solutions éprouvées aux problèmes typiques rencontrés lors du développement de logiciels. Pour C++, ces patrons sont particulièrement importants car le langage combine la programmation orientée objet, générique et bas niveau. Dans cet article, nous allons analyser les modèles de conception clés en C++ avec des exemples de code qui vous aideront à écrire un code plus flexible, réutilisable et maintenable.
Nous aborderons les trois principales catégories de patrons selon la classification GoF (Gang of Four) : les patrons de création, structurels et comportementaux. Chaque patron sera illustré par un exemple concret en C++.
1. Patrons de création (Creational Patterns)
Les patrons de création abstraient le processus de création d'objets, rendant le système indépendant de la manière dont les objets sont créés, composés et représentés.
1.1. Singleton (Singleton)
Le Singleton garantit qu'une classe n'a qu'une seule instance et fournit un point d'accès global à celle-ci. En C++, il est souvent implémenté via un objet local statique (thread-safe depuis C++11).
class Logger {public: static Logger& getInstance() { static Logger instance; // initialisation thread-safe return instance; }
void log(const std::string& message) { std::cout << "[LOG] " << message << std::endl; }
private: Logger() = default; Logger(const Logger&) = delete; Logger& operator=(const Logger&) = delete;};
// Utilisationint main() { Logger::getInstance().log("Application démarrée"); return 0;}Quand l'utiliser : gestion de configuration, pools de threads, systèmes de journalisation.
1.2. Factory Method (Méthode de fabrique)
La Factory Method définit une interface pour créer un objet, mais laisse les sous-classes décider quelle classe instancier.
class Product {public: virtual void use() = 0; virtual ~Product() = default;};
class ConcreteProductA : public Product {public: void use() override { std::cout << "Produit A" << std::endl; }};
class ConcreteProductB : public Product {public: void use() override { std::cout << "Produit B" << std::endl; }};
class Creator {public: virtual Product* createProduct() = 0; void someOperation() { Product* p = createProduct(); p->use(); delete p; } virtual ~Creator() = default;};
class CreatorA : public Creator {public: Product* createProduct() override { return new ConcreteProductA(); }};
class CreatorB : public Creator {public: Product* createProduct() override { return new ConcreteProductB(); }};Quand l'utiliser : lorsqu'une classe ne sait pas quels objets de sous-classes elle doit créer.
2. Patrons structurels (Structural Patterns)
Les patrons structurels décrivent comment les classes et les objets peuvent être combinés en structures plus grandes, tout en conservant flexibilité et efficacité.
2.1. Adapter (Adaptateur)
L'Adapter permet à des objets avec des interfaces incompatibles de fonctionner ensemble. En C++, il est souvent implémenté via l'héritage ou la composition.
// Interface cibleclass Target {public: virtual void request() const { std::cout << "Requête normale" << std::endl; } virtual ~Target() = default;};
// Classe à adapterclass Adaptee {public: void specificRequest() const { std::cout << "Requête spécifique" << std::endl; }};
// Adaptateur (via composition)class Adapter : public Target {private: Adaptee* adaptee_;public: Adapter(Adaptee* a) : adaptee_(a) {} void request() const override { adaptee_->specificRequest(); }};