La délégation de responsabilité par composition est une pratique de programmation où un objet d'une classe contient un objet d'une autre classe (ou classes) et utilise ces objets pour implémenter une partie de sa logique, au lieu d'hériter de leur interface.
Les premières versions de la POO mettaient l'accent sur l'héritage, mais avec le temps, la pratique a montré que la composition offre souvent une plus grande flexibilité, extensibilité et réduction de couplage entre les composants.
L'héritage lie les objets de manière rigide : les modifications dans la classe de base impactent tous les héritiers, les hiérarchies deviennent complexes et l'architecture fragile. La composition élimine ces problèmes, permettant de construire des systèmes plus fiables et maintenables.
En C++, la délégation est réalisée en incluant un objet d'une classe comme membre d'une autre classe. Dans la classe enveloppante, les méthodes de l'objet imbriqué sont appelées.
Exemple de code :
class Logger { public: void log(const std::string& msg) { std::cout << msg << std::endl; } }; class FileProcessor { Logger logger; // Composition public: void process(const std::string& filename) { logger.log("Traitement du fichier : " + filename); // ... } };
Caractéristiques clés :
La composition peut-elle remplacer complètement l'héritage ?
Non, l'héritage est nécessaire là où une relation "est un" (is-a) est requise, la composition — lorsque "a" (has-a). Par exemple, Button hérite de Widget, mais Car "a" Engine (composition).
Peut-on dans la composition modifier le comportement de la méthode déléguée ?
Oui, les méthodes de délégation peuvent être adaptées sans toucher à la classe de base. De plus, l'objet délégué peut être changé dynamiquement (par exemple, via un pointeur ou un pointeur unique).
La composition est-elle plus lente que l'héritage ?
Non, il n'y a pas de différence de performance dans la plupart des cas. Parfois, l'héritage ajoute des frais à cause des appels virtuels (vtable), tandis que la composition n'augmente que la taille de l'objet.
Dans le projet, toutes les fenêtres de dialogue hérité d'un DialogWindow commun. L'ajout de nouvelles logiques métier entraînait un code non fonctionnel dans tous les héritiers.
Avantages :
Inconvénients :
Les fonctions communes ont été extraites dans des classes séparées (journalisation, validation) qui sont injectées dans chaque dialogue par composition.
Avantages :
Inconvénients :