1. Contexte
De nombreuses applications financières nécessitent une analyse des performances des portefeuilles, suivie de diverses représentations en fonction des besoins : graphiques en ligne, diagrammes en barres, diagrammes circulaires, etc. Ces évaluations de performance sont souvent classées par catégories d'instruments financiers tels que les actions, les obligations, les options, etc… Traditionnellement, une sélection parmi toutes les combinaisons de catégories et de représentations, basée sur une interface commune, est utilisée pour générer un rapport selon les exigences du client. Cependant, l'extension des catégories dans ces deux dimensions indépendantes ne peut pas toujours être prévue exhaustivement dès le début du développement. L'introduction d'un nouvel instrument financier impliquerait la révision de toutes les démonstrations existantes. De même, l'ajout d'une nouvelle représentation graphique devrait être pris en compte pour tous les produits à évaluer. En somme, toute introduction de nouveaux algorithmes dans une dimension donnée pourrait potentiellement entraîner une augmentation exponentielle de la taille des hiérarchies, un problème classique causé par l'héritage simple. Cependant, ce problème ne concerne pas les interfaces obsolètes et diffère de celui résolu par le patron de conception adaptateur [1].
Notre approche logicielle vise à trouver une solution générique pour étendre les deux dimensions de manière indépendante, en utilisant la composition plutôt que l'héritage. Nous introduisons donc un nouveau patron de conception plus souple : le Pont (ou "bridge" en anglais), qui fait partie des conceptions structurelles [2] et vise à établir des relations simples entre les entités. Pour ce faire, une des dimensions est insérée dans une hiérarchie de classes distincte, permettant à la classe d'origine de référencer un objet de cette nouvelle hiérarchie, au lieu de regrouper tous les états et comportements au sein d'une même classe. Cela permet de diviser une grande classe ou un ensemble de classes complexes en deux hiérarchies distinctes : l'abstraction et l'implémentation, qui peuvent évoluer indépendamment l'une de l'autre.
Dans cet article, nous mettons en œuvre un pont pour résoudre le problème de génération de rapports avec des représentations variables pour différentes classes de produits financiers. Nous commençons par calculer les performances des instruments financiers, puis nous utilisons des outils de mise en forme graphique pour illustrer les résultats. Le pont établit le lien entre ces deux étapes indépendantes avant de produire les rapports de performance à évaluer. De plus, nous implémentons cette conception en Python 3 en respectant les principes SOLID [3].
2. Conception du pont
Le diagramme UML illustre le développement du Pont en Python 3. Ce patron de conception décompose l'interface et l'implémentation du composant en hiérarchies de classes orthogonales. Outre la classe "bridge" (pont), la structure comprend trois composants principaux : une interface au-dessus du pont, plusieurs classes concrètes en dessous du pont, et une abstraction d'implémentation comprise dans le pont, qui appelle les instances d'implémentation selon les besoins.
Basée sur la composition, la classe Bridge hérite de l'abstraction Interface et contient une référence vers la classe d'implémentation abstraite. Cette référence est initialisée avec une instance d'une classe d'implémentation concrète. Toute interaction ultérieure de l'Interface avec la classe d'implémentation est limitée à l'abstraction maintenue dans la classe commune Abstraction d'implémentation. Ainsi, le client interagit avec une classe dérivant de l'Interface, qui délègue toutes les requêtes à une classe concrète d'implémentation via le pont. Le pont agit comme un lien entre les deux abstractions qui évoluent indépendamment sans dépendance de schéma.
En d'autres termes, l'objet de la classe pont est le point de contact connu et utilisé par le client, tandis que l'objet délégué d'implémentation, ou "corps", est encapsulé en toute sécurité pour garantir qu'il puisse continuer à évoluer isolément, être entièrement remplacé ou partagé au moment de l'exécution.
Contrairement au patron de conception de la fabrique abstraite [4], qui propose la création d'objets complexes en séparant leurs caractéristiques intrinsèques via plusieurs interfaces avant de les assembler, notre Pont se charge de déléguer l'implémentation d'une abstraction dont l'évolution est indépendante de l'interface accessible par le client. Notre attention se porte uniquement sur l'appel effectué dans le Pont vers la méthode en délégation, qui ne fait partie d'aucune interface accessible par le client.
Dans notre contexte, le client cherche à appeler l'interface du compte rendu pour générer des rapports sur les valeurs ajoutées des actions, des obligations et des options répertoriées, qui sont représentées par des lignes de segments, des barres et des camemberts. Cependant, le calcul des valeurs ajoutées dépend uniquement de la classe de l'instrument financier et de sa référence à comparer, tandis que la mise en forme graphique nécessite simplement la prise en compte des valeurs calculées pour différentes démonstrations. Le Pont, dérivant de l'interface du compte rendu, déclare de nouveaux contrats pour les opérations qui gèreront le calcul lors de l'appel de la génération du rapport par le client, tout en déléguant l'illustration des résultats à son objet graphique. L'objet graphique du Pont, héritant d'une interface commune appelée à dessiner, définit les représentations variables en fonction de la demande, tandis que les classes raffinées de produits financiers dérivant du Pont mettent en œuvre concrètement les opérations enrichies dans le Pont.
En effet, en contournant l'héritage simple qui regrouperait le calcul et le dessin dans une seule classe, le Pont intervient pour découpler les deux dimensions orthogonales, réduisant ainsi le nombre de combinaisons nécessaires à neuf grâce à la composition pour la délégation.
3. Développement du pont
Dans notre exemple, nous cherchons à générer neuf comptes rendus de performance sur les valeurs ajoutées en combinant trois classes de produits financiers : actions, obligations et options listées, avec trois représentations de résultats : ligne de segments, barres et camemberts. Pour des raisons de confidentialité, nous simulons les prix d'achat et de vente en 2021, qui devraient normalement être récupérés à partir des bases de données de référence par des suites de valeurs fictives, de même que les prix de transaction réels. Pour la mise en forme des schémas des rapports, nous utilisons la bibliothèque "matplotlib" en Python 3.
Afin de simplifier notre démonstration, nous calculons la valeur ajoutée pour les actions et les obligations en soustrayant le prix réel du prix de vente de la référence, tandis que pour les options listées, le calcul de la valeur ajoutée repose sur la différence entre le spread (prix de vente moins prix d'achat) en valeur absolue dans la base de référence et celui des transactions réelles. Chaque rapport présente une figure montrant l'évolution des valeurs ajoutées mensuelles pendant l'année 2021.
Nous définissons l'interface IReport, qui déclare la méthode generate, appelée par le client pour générer un compte rendu. Cette interface est héritée par la classe Bridge ReportBridge, qui implémente la méthode generate en appelant la méthode draw pour dessiner sur son objet graphique. Ce dernier est une instance des classes LineChart, BarChart et PieChart, dérivées de la classe abstraite AbstractGraphic, et prend les arguments nécessaires pour tracer les figures dans les rapports. Les classes EquityReport, FixedIncomeReport et ListedOptionReport sont des classes concrètes de ReportBridge, raffinant les méthodes nécessaires liées aux différents calculs des valeurs ajoutées mensuelles.
Toutes les requêtes du client sont simplement déléguées par la classe d'interface à la classe d'implémentation encapsulée. Nous instancions une classe concrète de ReportBridge, qui prend les arguments sur les prix de transaction, la devise et l'année, ainsi qu'un objet graphique encapsulé dérivant de AbstractGraphic, avant d'appeler la méthode generate. Ainsi, les rapports des valeurs ajoutées en euros pour 2021 seront disponibles en sortie.
Voici les résultats affichés dans la console lorsque nous demandons par défaut à l’objet graphique de sauvegarder les rapports en image au format png dans le répertoire courant.
Les neuf comptes rendus sont bien générés séquentiellement, au fur et à mesure que les méthodes get_equity_reports, get_fixed_income_reports, get_listed_option_reports sont appelées.
En réalité, le Pont incarne l'essence de l'idiome "poignée/corps" en conception. La "poignée" représente notre classe Bridge. Elle est perçue par l'utilisateur comme la classe réelle, tandis que le travail réel s'effectue dans le "corps" à travers nos objets graphiques encapsulés. Cet idiome illustre le partage d'une seule ressource par plusieurs classes qui en contrôlent l'accès. En effet, quel que soit l'enrichissement des classes de produits, les classes graphiques restent inchangées, réutilisables et extensibles, grâce à leur exposition limitée au pont via la méthode draw, déclarée dans leur interface d'implémentation.
4. Conclusion
Dans cet article, nous débutons en mettant en lumière la pertinence du patron de conception Pont pour notre contexte spécifique, en explorant les mécanismes de sa famille de patrons de conception structurelle. À travers son diagramme UML, nous soulignons la nécessité d'encapsuler une référence à l'intérieur du Pont en divisant les fonctions indépendantes. En réalité, le Pont reflète l'idée de l'idiome "poignée/corps", qui décompose une abstraction complexe en classes plus petites et plus gérables. Par ailleurs, notre exemple est implémenté en Python 3, et les résultats obtenus lors de l'implémentation répondent à nos attentes par rapport à ce patron de conception structurelle.
Références
[1] https://invivoo.com/blog/adaptateur-design-pattern
[2] https://www.invivoo.com/blog/design-patterns-patrons-conception/
[3] https://www.invivoo.com/blog/lart-clean-code-environnement-java/