Refactoring .NET avec UML, suite et fin

le 12/11/2007 par Alain Buzzacaro
Tags: Évènements

Comme promis, voici la suite et fin de notre article sur l'expressivité d'UML. Pour poursuivre l'histoire de notre refactoring croustillant, nous zoomons sur le diagramme de classes macroscopique du logiciel Octopus.

Parviendra-t-on à comprendre des patterns métiers ou techniques d'Octopus grâce au retro engineering UML ? Mieux, arrivera-t-on au bout de notre refactoring du Core Domain... ou pas ?

Messaoud OUBECHOU
Article paru en presse dans "Programmez !" en octobre 2007, dossier consacré à UML.

La semaine dernière, nous prenions connaissance de la conception générale d'Octopus.

Dans cette seconde et dernière partie de cet article, nous zoomons sur différentes parties du diagramme macroscopique pour voir si on parvient à comprendre des patterns métiers ou techniques d'Octopus et si UML permet ou pas d'arriver au bout de notre refactoring du Core Domain.

Zoomons sur les soleils

DataSet typés en soleils

Le diagramme fait apparaître quelques soleils microscopiques.
Il s'agit en fait des jeux de classes associées aux DataSets typés générés avec Visual Studio et .NET. Le regroupement visuel est intuitif et la lecture visuelle est plus rapide que la lecture des longues classes générées.
Zoomons sur l'un d'entre eux pour comprendre leur structure en détail.

Zoomons sur un des soleils

Détail du DataSet typé des mouvements élémentaires

L'un de ces soleils représente justement le DataSet des mouvements élémentaires : nous voyons très rapidement un DataSet typé, et ses relations avec des DataTables (ElementaryMvtsDataTable) / DataRows (ElementaryMvtsRow), et les évènements délégués.

Le cas d'usage le plus fréquent des DataSet typés est leur binding sur des contrôles graphiques. Dans un refactoring de code sur les mouvements élémentaires, nous comprenons donc qu'il y a fort à parier que des formulaires seront impactés...

Ce détail permet d'identifier facilement les champs métier du DataSet : Date, Account_number_debit, Account_number_credit, Amout,...Mais on est aussi pollué par la myriade de méthodes techniques générées par le framework lors de la génération du DataSet, comme ReadXmlSerialize() par exemple. Ces méthodes sont très nombreuses et n'apportent rien à la compréhension de la classe. Elles freinent la compréhension des informations métier utiles : en UML, ces deux types d'information ne sont pas différenciés.

Zoomons encore un peu plus...
Pour notre refactoring, la classe la plus importante est naturellement la classe des mouvements élémentaires, ElementaryMvt, présentée ci-dessous.
Ici, la direction du mouvement élémentaire est stockée en caractère ‘C' ou ‘D'. Elle fait effectivement apparaître le pattern à refactorer. On comprend aussi instantanément que c'est l'interface IElementaryMvt qui devra commencer à être refactorée, et que seule la classe ElementaryMvt sera directement impactée car elle seule hérite de cette interface.

Classe des mouvements élémentaires

En naviguant dans les classes commençant par ‘Elementary', nous trouvons une classe nommée ElementaryMvtToExport. Celle-ci est directement reliée à une classe Direction. Nous comprenons donc que ces classes sont à remanier : la classe Direction est vouée à disparaître dans le nouveau pattern des mouvements élémentaires.
Ces classes illustrent aussi une petite hétérogénéité dans la gestion de la direction : tantôt un caractère, tantôt une classe Direction. Fort heureusement, seule ElementaryMvtToExport sera impactée par la suppression de la classe Direction...

Les mouvements élémentaires à exporter

Zoomons final sur la classe EventProcessor

Classes de traitement des évènements sur contrat

Le pattern singleton est implémenté dans la classe ChartOfAccount et est utilisé dans la classe EventProcessor.
Nous observons aussi la méthode publique " FireEvent (e : Event) : MovementSet " qui redirige les appels aux méthodes privées obéissant à la signature :
" - NomDeLaMethode (e : xxxEvent) : MovementSet ".
Or ces méthodes sont au coeur de la logique de fonctionnement d'Octopus : ce sont elles qui traduisent les évènements sur contrat en paires d'écritures comptables.

Nous venons là de trouver le lieu principal de refactoring, mais UML ne nous sera d'aucune aide supplémentaire. En effet, la plus fine granularité d'UML est celle d'attributs et méthodes, et non la ligne de code, or le traducteur d'évènements est codé dans les méthodes d'EventProcessor...

La suite et fin du refactoring se déroulera donc dans notre Visual Studio préféré. Et nous commencerons (1) par le refactoring du corps des méthodes de tests unitaires... qu'UML ne connaît pas plus que le contenu des méthodes d'EventProcessor....

Conclusion : un modèle utile, mais pas suffisant
En résumé, le rétro engineering de code source est assez facile à opérer. Certains outils permettent très facilement de produire une toile d'araignée en guise de diagramme de classes. Mais d'autres parviennent assez habilement à représenter cette complexité. Le résultat exprime la structure de la conception grâce à des layout originaux.

D'une part, comme nous l'avons vu, cela permet de comprendre des patterns techniques de conception, tels que : les classes et interfaces centrales aux soleils, les DataSets typés, le singleton, les centres de gravité technique,... Pour cela, on procède par zooms successifs et par navigation entre classes du diagramme.
D'autre part, nous avons vu que cela permet aussi de visualiser le code de manière plus synthétique que la lecture des nombreuses lignes de code correspondantes. Avec le modèle physique de données, ce sont probablement les deux documents les plus utiles.

Les diagrammes de classe UML sont donc utiles, mais comme nous venons de le voir, ils ne nous ont pas permis de comprendre tous les patterns, dont certains sont pourtant essentiels au logiciel. C'est l'exemple typique du pattern " interpréteur comptable " d'Octopus que l'on capture à l'intérieur les méthodes de la classe EventProcessor. De plus, lorsqu'il s'agit pour effectuer le remaniement du code source, UML n'est plus adapté puisque UML ne s'intéresse pas au code des méthodes de l'implémentation.

A ce jour, le travail au niveau de la ligne de code reste incontournable pour remanier du logiciel, et par là même pour le faire évoluer.

-- Messaoud OUBECHOU et Guillaume HOLLER, architectes.
Centre de Compétences .NET


(1) Hé oui, TDD oblige...! ;-)