
Maintenant que vous savez créer des objets robustes et sécurisés, l'heure est venue de les faire interagir. Car dans un système réel comme l'application ChessMaster Pro, les entités n'existent pas dans le vide ; elles interagissent constamment pour réaliser la logique métier. Un Joueur doit participer à une Partie, et une Partie doit avoir lieu dans une Ronde, elle-même organisée au sein d'un Tournoi.
Si la POO vise la modularité et la scalabilité, nous devons apprendre à structurer ces liens complexes de manière logique et maintenable.
Notre défi, en particulier pour ChessMaster Pro, est de modéliser la structure hiérarchique des événements : le Tournoi est l'objet principal, mais il doit absolument contenir des Rondes, qui, elles-mêmes, contiennent des Matchs. Ce type de relation, où la partie dépend du tout, est modélisé par la Composition, et c'est ce que nous allons explorer en détail pour vous permettre de construire des systèmes complexes et cohérents. Vous apprendrez comment les objets peuvent s'associer entre eux pour former des ensembles solides.
Pour structurer votre application ChessMaster Pro, il est essentiel de bien modéliser les relations entre vos classes. Il existe plusieurs types de relations, mais trois sont particulièrement courantes : l'Association, l'Agrégation et la Composition.
C'est une simple référence : l'objet A connaît l'objet B.
Par exemple, dans ChessMaster Pro, un Tournoi utilise plusieurs entités Joueur, ou une Partie utilise deux Joueurs. L'existence d'un Joueur n'est pas dépendante de l'existence du Tournoi.
Cette relation peut être unidirectionnelle (A connaît B) ou bidirectionnelle (A connaît B, et B connaît A). L'association est souvent implémentée en faisant référence à un objet dans les attributs d'un autre (par exemple, la classe Tournoi possède un attributlisteParticipantsqui est une collection d'objets Joueur).
Mais qu'en est-il lorsque le lien est structurel et vital ?
C'est là que la Composition et l'Agrégation entrent en jeu, car elles représentent toutes deux une relation "tout-partie".
1- La Composition (Relation Forte) : La composition est une forme très spécifique d'association qui représente une relation "tout-partie" très forte. Elle est utilisée lorsque la relation peut être exprimée par la phrase "possède un" (has-a) et que le lien est fort. La notion clé est la dépendance existentielle. Un objet est constitué d'autres objets qui lui sont indispensables. L'existence de la "partie" dépend de l'existence du "tout". Si l'objet principal (le composite) est détruit, les objets qui le composent (les composants) sont détruits avec lui.
2- L'Agrégation (Relation Souple) : L'agrégation est également une relation "tout-partie", mais elle est plus souple que la composition. Le lien est plus faible, et l'objet "partie" peut exister indépendamment de l'objet "tout". Aucun objet n'a de propriété exclusive sur l'autre.
En réfléchissant à la force de ces liens (Composition = éléments dépendants et destruction commune ; Agrégation = conteneur d'entités indépendantes ; Association = simple connaissance), vous modéliserez des systèmes logiques et faciles à comprendre pour toute votre équipe. Ce choix est fondamental car il impacte directement la durée de vie et la maintenance de votre application.
Maintenant que nous avons identifié la Composition comme étant la relation idéale pour structurer notre hiérarchie Tournoi $\rightarrow$ Ronde $\rightarrow$ Match, nous devons passer au "Comment".
Comment implémenter cette relation dans le code, surtout quand un objet contient plusieurs objets de la même nature ?
Dans le monde de la POO, les relations ne sont pas toujours un-à-un. Elles peuvent être un-à-plusieurs, comme un Tournoi contenant une liste de Rondes, ou plusieurs-à-plusieurs.
Avant de commencer à coder, il est toujours bénéfique de visualiser la structure des données. Cela vous oblige à réfléchir de manière opérationnelle pour déterminer précisément les informations essentielles dont vous avez besoin. Vous pouvez utiliser un diagramme UML (qui est une façon de documenter ses classes) pour formaliser la hiérarchie.
Pour ChessMaster Pro, cette visualisation se traduit ainsi :

UnTournoicontient (par Composition) plusieurs instances deRound(relation un-à-plusieurs).
Chaque instance deRoundcontient à son tour plusieurs instances deMatch.
ChaqueMatchfera référence à deux instances dePlayer.
L'implémentation concrète de la Composition passe par la définition d'attributs au sein de la classe composite (le "tout") qui contiennent les instances des classes composantes (les "parties"). Puisque nous sommes dans un cas "un-à-plusieurs", ces attributs seront des collections (listes, tableaux ou structures de données similaires).
La classeTournoiaura un attribut, par exemplelisteDesRondes, qui est une liste d'objetsRound.
La classeRoundaura un attribut, par exemplelisteDesMatchs, qui est une liste d'objetsMatch.
En utilisant la Composition, vous créez une unité autonome et cohérente. La logique de gestion des rondes est entièrement contenue dans l'objet Tournoi, et la logique de gestion des matchs est entièrement contenue dans l'objet Round. Cette modularité permet à chaque classe d'être développée, testée et maintenue de manière isolée. Si un bug est détecté dans la gestion d'un match, vous savez exactement où chercher, sans affecter l'ensemble de la structure du tournoi, garantissant ainsi une maintenance facilitée.
En structurant ainsi votre code autour d'objets qui reflètent la hiérarchie réelle (Joueur, Match, Ronde, Tournoi), vous améliorez grandement la lisibilité du logiciel.
Dans la hiérarchie que nous venons de modéliser, nous avons établi que le Tournoi connaît ses Rondes, et la Ronde connaît ses Matchs (Composition). C'est une relation essentiellement descendante.
Par exemple, si la classe Match contient une référence aux deux Player, elle peut accéder à leurs attributs (comme le classementElo) ou appeler leurs méthodes (commejouerCoup()).
Pour que le système soit plus complet, il est possible qu'un Player doive également connaître l'historique des Matchs auxquels il a participé. Dans ce cas, nous aurions :
LeMatchcontient une référence auxPlayer(Unidirectionnel de Match vers Player, ou Bidirectionnel si les deux se connaissent).
LePlayer(potentiellement) contiendrait une liste desMatchs(Référence en retour).
Lorsque vous mettez en œuvre des liens bidirectionnels, vous devez faire preuve de vigilance, car un objet doit être créé avant l'autre pour pouvoir se lier dans les deux directions.
Souvenez-vous du cycle de vie d'un objet : il commence par la création, où le Constructeur est exécuté. Le rôle fondamental du constructeur__init__ est d'initialiser l'état de l'objet, en lui fournissant toutes les informations nécessaires pour qu'il soit valide dès le départ.
Si une Ronde doit impérativement disposer d’une référence vers son Tournoi parent pour être considérée comme complète, cette référence doit être transmise en paramètre lors de l’appel au constructeur de Ronde :
nouvelle_ronde = Ronde(tournoi_parent, numero_ronde)
L’objet tournoi_parent doit donc avoir été instancié au préalable. La classe Ronde enregistre ensuite cette référence comme l’un de ses attributs internes. Si, par la suite, le Tournoi doit connaître cette nouvelle Ronde (par exemple pour l’ajouter à sa liste_des_rondes), cet ajout doit être effectué soit juste après la création de la Ronde, soit directement dans le constructeur du Tournoi si celui-ci gère lui-même l’instanciation de ses différentes composantes.
Vous avez désormais la capacité de lier vos objets par Composition et de gérer des listes d'instances.
Cependant, à mesure que l'application ChessMaster Pro grandit, certaines tâches de haut niveau deviennent très complexes, impliquant la manipulation de vastes collections d'objets, ou nécessitant une logique qui ne concerne pas un seul objet, mais l'ensemble du système.
Où placer cette logique complexe ?
Nous avons vu que les méthodes d'instance sont conçues pour agir sur les attributs d'un objet spécifique (ex :joueur1.mettreAJourClassement(...)). Les méthodes de classe (statiques) sont associées à la classe elle-même, sont indépendantes des objets et ne peuvent pas accéder aux attributs d'instance non statiques.
Pour les tâches complexes impliquant des collections ou des interactions globales, il est généralement préférable de ne pas utiliser de méthodes de classe.
Ce type de logique n'appartient pas à la classePlayer(car elle n'agit pas seulement sur un seul joueur) ni à la classe Tournoi si nous voulons maintenir cette dernière propre.
La solution consiste à suggérer d'utiliser une classeManager.
Une classe Manager (par exemple, unPlayerManagerou unMatchmakingManager) est une classe dédiée dont la responsabilité est de gérer une collection d'objets et d'implémenter les logiques métier complexes qui affectent l'ensemble de cette collection.
Les classesManagersont essentielles pour maintenir une distinction claire entre les classes et les instances. La classePlayerreste le plan de l'entité Joueur, et l'instance joueur1 reste le joueur concret. La classePlayerManager, quant à elle, prend la responsabilité de manipuler ces instances de joueurs dans le cadre de tâches globales. Cette délégation des responsabilités est un aspect crucial de la conception logicielle orientée objet : elle renforce la modularité de votre code.

En centralisant la logique de haut niveau dans des classesManager, vous évitez que les classes d'entité de base (commePlayer) soient surchargées de responsabilités qui ne sont pas intrinsèques à leur propre état. Vous facilitez grandement l'évolutivité du système. Si ChessMaster Pro doit changer son algorithme de matchmaking, vous savez exactement où la modification doit être effectuée : uniquement dans la classeManagercorrespondante. Cette approche vous permet de construire une application qui peut grandir, se modifier et perdurer dans le temps.

La start-up ChessMaster Pro a besoin d'intégrer les joueurs dans une structure de tournoi complète. La hiérarchie doit être : Tournoi $\rightarrow$ Round $\rightarrow$ Match $\rightarrow$ Player. Nous allons utiliser la Composition pour les liens structurels forts (Tournoi $\rightarrow$ Round et Round $\rightarrow$ Match) et l'Agrégation pour les références faibles (Match $\rightarrow$ Player).
Implémentez les classesTournoi,RoundetMatch(en Pseudocode/Python, dans la continuité des chapitres précédents).
Assurez-vous que les instances deTournoicontiennent des références à des instances deRound(Composition, via une liste).
Assurez-vous que les instances deRoundcontiennent des références à des instances deMatch(Composition, via une liste).
Assurez-vous que les instances deMatchcontiennent des références à des instances dePlayer(Agrégation, via une simple référence, car le joueur existe indépendamment du match).
Implémentez des constructeurs (__init__) pour initialiser ces relations dès la création.
La Composition est une forme d'association forte où l'existence d'un objet "partie" dépend de celle de l'objet "tout" (relation indispensable), contrairement à l'Agrégation où l'objet "partie" peut exister indépendamment.
La modélisation des relations Un-à-Plusieurs (comme un Tournoi possédant plusieurs Rondes) est implémentée en utilisant des collections d'instances (listes) comme attributs au sein de la classe principale.
Lors de l'implémentation de relations bidirectionnelles (lorsque deux objets se connaissent mutuellement), l'ordre de création des instances et l'initialisation via le constructeur sont cruciaux pour établir correctement les références.
Pour la gestion des collections d'objets et l'implémentation de logiques complexes de haut niveau (comme le matchmaking), il est recommandé de déléguer cette responsabilité à une classe Manager dédiée, qui maintient la distinction entre la classe et l'instance.
Après avoir découvert comment structurer et relier vos objets, il est temps d’explorer un autre pilier majeur de la programmation orientée objet : l’héritage, qui vous permettra de réutiliser efficacement votre code et d’en améliorer la cohérence.