vendredi 18 décembre 2009

Le mécanisme de réflexion – partie 2

Une extension du design pattern Strategy grâce au mécanisme de réflexion en .NET

Le contexte
Dans la société où je travaille, nous avons développé récemment un module d’acquisition de documents EDI. Ce module doit rematérialiser le document (générer une image à partir du contenu du fichier), puis l’injecter dans une chaîne de traitement métier pour analyse et export vers des systèmes informatiques cibles, genre ERP ou GED. Les documents traités sont des factures.
Un des principaux problèmes liés à l’EDI est la multiplicité des formats existant : EDIFACT, ANSI X12 pour les plus connus, mais surtout les formats non standards mais déjà abondamment utilisés dans des cadres de partenariats de dématérialisation, notamment dans le monde de la grande distribution. Le module doit donc pouvoir s’adapter facilement à un nouveau format EDI que nous découvririons lors de la mise en œuvre chez un client.

Dans les grandes lignes, le module est composé des trois fonctions suivantes :
1. Lecture du fichier EDI source
2. Rematérialisation du document
3. Injection dans la chaîne de traitement métier.

Solution 1
Nous optons pour le design pattern Strategy pour implémenter la lecture du fichier EDI source. La célèbre interface « Strategy »  publiera une méthode qui pourra être implémentée autant de fois que nous aurons formats à gérer. Chaque implémentation concrète de l'interface crée une représentation objet du document EDI qui sera ensuite utilisée pour la rematérialisation et l’injection dans la chaîne de traitement métier.
Donc c’est parti, on développe, on teste, on compile, on livre et on met en production !
Quelques temps plus tard, le client annonce un nouveau format à gérer. C’est facile, le pattern Strategy permet d’ajouter de quoi lire ce nouveau format sans prendre de risque avec l’existant. Donc pas de problème, on développe donc une nouvelle implémentation de l’interface « Strategy », on modifie le programme principal, puis on recompile et on livre une nouvelle version du module d’EDI et le tour est joué !
Malheureusement, le client ne l’entend pas de cette manière ! Nouvelle version du module d’EDI veut dire nouvelle exécution des plans de tests d’intégration avec son SI. Il doit mobiliser son informatique interne, ses chefs de projets fonctionnels. Les délais de mise en production vont se chiffrer en semaines, voire en mois. Qui va payer ? Vous avez beau arguer que vous avez utilisé le design pattern approprié et qu’il n’y a pas de risques, il n’y a rien à faire. La situation avec le client s’est indéniablement dégradée !

Solution 2
L’idéal serait que la prise en charge d’un nouveau format se résume à déposer un nouvel assemblage et mettre à jour un paramètre décrivant dans quelles circonstances utiliser ce nouvel assemblage. Ceci est possible grâce à la réflexion. Si chaque implémentation de l’interface Strategy est isolée dans un assemblage, il suffit de récupérer dans les paramètres l’assemblage à charger (il peut être fonction de l’émetteur du document, par exemple) et la réflexion se chargera du reste.
L’interface Strategy s’appelle ici IEdiInvoiceParser. Elle propose une méthode Parse() qui prend un flux de données en entrée et renvoie un objet métier Invoice en sortie. En voici le code en C# :



Le code ci-dessous montre comment créer une instance d’une classe à partir du nom de l’assemblage et de la classe.



 

Noter la puissance de l’instruction CreateInstanceAndUnWrap() qui charge dans le domaine courant l’assemblage désigné, puis crée une instance du type passé en paramètre et enfin renvoie une référence vers cette instance. Naturellement, pour que le cast fonctionne, il faut que ce type implémente l’interface IEdiInvoiceParser définie ci-dessus !
Même si l’opération tient en une seule ligne, il peut être intéressant de l’isoler dans une classe. Dans la pratique, cette classe pourra également récupérer les informations assemblyName (nom complet de l’assemblage à charger) et className (nom de la classe préfixé par le namespace) à partir d’autres informations comme la source du fichier EDI, par exemple.

L’utilisation est des plus simples et des plus rapides. Il n’y a plus qu’à laisser fonctionner le polymorphisme !




 

Pour conclure...
La solution 2 est meilleure car elle allie vitesse d’exécution (grâce au polymorphisme) et découplage (grâce à la réflexion). 
Une de ses limites est que les assemblages contenant les stratégies de lecture ne peuvent pas être déchargés car ils sont chargés dans le domaine courant de l’application. Cela pourrait donc poser des problèmes de consommation mémoire si on utilise cette technique pour implémenter un système de plugin. Dans ce cas, il sera préférable de charger l’assemblage dans un autre domaine, qui lui pourra être déchargé à la demande.

Aucun commentaire:

Enregistrer un commentaire