Intentation
- Composer des objets en structures arborescentes pour représenter des hiérarchies entières. La composition permet aux clients de traiter les objets individuels et les compositions d’objets de manière uniforme.
- Composition récursive
- « Les répertoires contiennent des entrées, dont chacune pourrait être un répertoire. »
- 1-à-plusieurs « a » jusqu’à la hiérarchie « est un »
Problème
L’application doit manipuler une collection hiérarchique d’objets « primitifs » et « composites ». Le traitement d’un objet primitif est traité d’une seule façon, et le traitement d’un objet composite est traité différemment.Avoir à interroger le « type » de chaque objet avant de tenter de le traiter n’est pas souhaitable.
Discussion
Définir une classe de base abstraite (Component) qui spécifie le comportement qui doit être exercé uniformément sur tous les objets primitifs et composites. Sous-classez les classes Primitive et Composite à partir de la classe Composant. Chaque objet Composite se « couple » uniquement au type abstrait Component alors qu’il gère ses « enfants ».
Utilisez ce motif chaque fois que vous avez des « composites qui contiennent des composants,dont chacun pourrait être un composite ».
Les méthodes de gestion des enfants devraient normalement être définies dans la classe Composite. Malheureusement, le désir de traiter les Primitives et les Composites de manière uniforme nécessite que ces méthodes soient déplacées vers la classe abstraite Component. Voir la section « Opinions « ci-dessous pour une discussion sur les questions de « sécurité » par rapport aux questions de « transparence »
Structure
Composites qui contiennent des composants, chacun d’entre eux pouvant être unComposite.
Menus qui contiennent des éléments de menu, chacun d’entre eux pouvant être un menu.
Gestionnaires de mise en page de l’interface graphique en ligne-colonne qui contiennent des widgets, chacun d’entre eux pouvant être un gestionnaire de mise en page de l’interface graphique en ligne-colonne.
Des répertoires qui contiennent des fichiers, chacun d’entre eux pouvant être un répertoire.
Des conteneurs qui contiennent des éléments, chacun d’entre eux pouvant être un conteneur.
Exemple
Le composite compose des objets en structures arborescentes et permet aux clients de traiter les objets individuels et les compositions de manière uniforme. Bien que l’exemple soit abstrait, les expressions arithmétiques sont des Composites. Une expression arithmétique est constituée d’un opérande, d’un opérateur (+ – * /) et d’un autre opérande. L’opérande peut être un nombre ou une autre expression arithmétique. Ainsi, 2 + 3 et (2 + 3) + (4 * 6) sont toutes deux des expressions valides.
Liste de vérification
- Assurez-vous que votre problème porte sur la représentation de relations hiérarchiques « tout-partie ».
- Considérez l’heuristique « Les conteneurs qui contiennent des conteneurs, dont chacun pourrait être un conteneur. » Par exemple, « les assemblages qui contiennent des composants, chacun d’entre eux pouvant être un assemblage ». Divisez vos concepts de domaine en classes de conteneurs et en classes de conteneurs.
- Créez une interface du « plus petit dénominateur commun » qui rend vos conteneurs et conteneurs interchangeables. Elle doit spécifier le comportement qui doit être exercé uniformément sur tous les objets conteneur et containee.
- Toutes les classes conteneur et containee déclarent une relation « est un » à l’interface.
- Toutes les classes conteneur déclarent une relation « a un » de un à plusieurs à l’interface.
- Les classes conteneur tirent parti du polymorphisme pour déléguer à leurs objets containee.
- Les méthodes de gestion des enfants devraient normalement être définies dans la classe Composite. Malheureusement, le désir de traiter uniformément les objets Leaf et Composite peut exiger que ces méthodes soient promues dans la classe abstraite Component. Voir le Gang ofFour pour une discussion sur ces compromis entre « sécurité » et « transparence ».
Règles du pouce
- Composite et Decorator ont des diagrammes de structure similaires, reflétant le fait que les deux reposent sur la composition récursive pour organiser un nombre illimité d’objets.
- Composite peut être parcouru avec Iterator. Visiteur peut appliquer une opération sur un Composite. Un composite peut utiliser la chaîne de responsabilité pour permettre aux composants d’accéder aux propriétés globales via leur parent. Elle peut également utiliser un décorateur pour surcharger ces propriétés sur les parties de la composition. Il pourrait utiliser Observer pour lier une structure d’objet à une autre et State pour laisser un composant changer son comportement lorsque son état change.
- Composite peut vous permettre de composer un Mediator à partir de plus petits morceaux par le biais de la composition récursive.
- Decorator est conçu pour vous permettre d’ajouter des responsabilités aux objets sans sous-classer. Composite ne se concentre pas sur l’embellissement mais sur la représentation. Ces intentions sont distinctes mais complémentaires.Par conséquent, Composite et Decorator sont souvent utilisés de concert.
- Flyweight est souvent combiné avec Composite pour implémenter des leafnodes partagés.
Opinions
Tout l’intérêt du pattern Composite est que le Composite peut être traité atomiquement, tout comme une feuille. Si vous voulez fournir un protocole Iterator, très bien, mais je pense que c’est en dehors du pattern lui-même. Au cœur de ce modèle est la capacité pour un client d’effectuer des opérations sur un objet sans avoir besoin de savoir qu’il y a beaucoup d’objets à l’intérieur.
Etre capable de traiter une collection hétérogène d’objets de façon atomique (ou transparente) exige que l’interface de « gestion des enfants » soit définie à la racine de la hiérarchie des classes Composite (la classe abstraiteComponent). Cependant, ce choix vous coûte de la sécurité, car les clients peuvent essayer de faire des choses insignifiantes comme ajouter et retirer des objets des objets feuilles. D’autre part, si vous « concevez pour la sécurité », l’interface de gestion des enfants est déclarée dans la classe Composite, et vous perdez la transparence parce que les feuilles et les Composites ont maintenant des interfaces différentes.
Les implémentations Smalltalk du patron Composite n’ont généralement pas l’interface de gestion des composants dans l’interface Component,mais dans l’interface Composite. Les implémentations C++ ont tendance à la mettre dans l’interface Component. Il s’agit d’un fait extrêmement intéressant, auquel je réfléchis souvent. Je peux proposer des théories pour l’expliquer, mais personne ne sait avec certitude pourquoi c’est vrai.
Mes classes de composants ne savent pas que les composites existent. Elles ne fournissent aucune aide pour naviguer dans les Composites, ni aucune aide pour modifier le contenu d’une Composite. C’est parce que je voudrais que la classe de base(et tous ses dérivés) soit réutilisable dans des contextes qui ne nécessitent pas de Composites. Lorsqu’on me donne un pointeur de classe de base, si j’ai absolument besoin de savoir s’il s’agit ou non d’une Composite, j’utiliserai dynamic_cast
pour le savoir. Dans les cas où dynamic_cast
est trop coûteux, j’utiliserai un Visitor.
Doléance commune : « si je pousse l’interface Composite vers le bas dans la classeComposite, comment vais-je énumérer (c’est-à-dire traverser) une structure complexe ? ». Ma réponse est que lorsque j’ai des comportements qui s’appliquent à des hiérarchies comme celle présentée dans le patron Composite, j’utilise typiquement Visitor, donc l’énumération n’est pas un problème – le Visitork sait dans chaque cas, exactement à quel type d’objet il a affaire. LeVisiteur n’a pas besoin que chaque objet fournisse une interface d’énumération.
Composite ne vous oblige pas à traiter tous les composants comme des Composites. Il vous dit simplement de mettre toutes les opérations que vous voulez traiter « uniformément » dans la classe Component. Si les opérations d’ajout, de suppression et autres opérations similaires ne peuvent pas, ou ne doivent pas, être traitées uniformément, alors ne les placez pas dans la classe de base Component. Rappelez-vous, en passant, que le diagramme de structure de chaque modèle ne définit pas le modèle ; il décrit simplement ce qui, dans notre expérience, est une réalisation commune de celui-ci. Ce n’est pas parce que le diagramme de structure de Composite montre des opérations de gestion des enfants dans la classe de base Component que toutes les implémentations du patron doivent faire de même.
Soutiens notre site web gratuit et possède l’eBook !
- 22 design patterns et 8 principes expliqués en profondeur
- 406 pages bien structurées, faciles à lire, sans jargon
- 228 illustrations et diagrammes clairs et utiles
- Une archive avec des exemples de code en 4 langues
- Tous les appareils sont pris en charge : Formats EPUB/MOBI/PDF
En savoir plus…