Diagrammes de Classe UML - John COLIBRI. |
- résumé : présentation des diagrammes de classe UML qui sont l'un des outils les plus efficaces pour l'analyse et la conception objet, la documentation et l'exploration de librairies objet
- mots clé : diagramme de classe - UML - analyse et conception objet - Together - reverse engineering
- logiciel utilisé : Windows XP personnel, Delphi 6.0
- matériel utilisé : Pentium 2.800 Mhz, 512 Meg de mémoire, 250 Giga disque dur
- champ d'application : Delphi 1 à 5, Delphi 6, Delphi 7, Delphi 2006, Turbo Delphi, Delphi 2007 et Delphi 2009 sur Windows, Delphi Prism
- niveau : développeur Delphi, tout développeur objet
- plan :
1 - Diagrammes de Classe UML Les diagrammes de classe UML se sont imposés depuis une dizaine d'années comme l'outil de représentation des Classes.
Ce type de diagramme, très facile à comprendre, offre de nombreux avantage: - il met en évidence la structure des Classes et leurs relations
- c'est un outil de documentation fabuleux pour
- mettre en évidence les points clés d'une Classe
- explorer l'organisation d'une librairie
- présenter une librairie
2 - Exemple de Diagrammes de Classe UML 2.1 - Une librairie de dessin graphique Pour présenter les diagrammes de classe, nous allons d'abord prendre une (toute
petite) librairie graphique, capable d'afficher des figures géométriques sur le Canvas: - deux types de figures, des rectangles et des ellipses
- les figures sont créées par clic gauche (les rectangles) ou droit (les ellipses)
- les figures peuvent être déplacées à l'aide des flèches du clavier
2.2 - Organisation de la librairie Comme nous souhaitons déplacer les figures, il faut stocker leurs paramètres
dans une structure. Nous allons donc utiliser une liste de figures. Chaque figure sera définit par un point d'ancrage, et des paramètres propres à chaque figure. Pour que les figures puissent appartenir à la même structure, if faut que
chaque figure hérite d'une même figure ancêtre. En Résumé - nous définissons une figure de base c_shape, qui contient les coordonnées de l'ancre. Les méthodes essentielles sont
- dessiner la figure sur un tCanvas
- déplacer la figure
- notre structure est une liste de c_figure
- les deux figures c_rectangle et c_ellipse descendent de cette figure. Nous
souhaitons aussi pouvoir agrandir automatiquement tous les rectangles
Notez qu'ici les doigts nous démangent de présenter les diagrammes UML. Comme c'est l'objet de l'article, nous les présenterons, exceptionnellement, après la
présentation du code
2.3 - Le Code Delphi La figure de base est définie par:
Type c_base_shape = Class
m_x,m_y: integer;
m_c_canvas_ref: tCanvas;
Constructor create_base_shape(p_x, p_y: Integer;
p_c_canvas_ref: tCanvas);
Procedure draw_shape; Virtual; Abstract;
Procedure move_shape(p_delta_x, p_delta_y: Integer);
End; // c_base_shape |
Voici la définition de la liste des figures:
Type c_shape_list=
Class
m_shape_array: Array Of c_base_shape;
m_shape_count: integer;
Constructor create_shape_list;
Procedure draw_shape_list;
Procedure add_shape(p_c_base_shape: c_base_shape);
Procedure move_shape(p_delta_x, p_delta_y: Integer);
Destructor Destroy; Override;
End; // c_shape_list |
Et nos deux types de figures sont définies ainsi:
Type c_rectangle_shape =
Class(c_base_shape)
m_width, m_height: Integer;
Constructor create_rectangle_shape(p_x, p_y: Integer;
p_c_canvas_ref: tCanvas;
p_width, p_height: Integer);
Procedure draw_shape; Override;
Procedure resize(p_scale: Integer);
End; // c_rectangle_shape c_ellipse_shape =
Class(c_base_shape)
m_radius: Integer;
Constructor create_ellipse_shape(p_x, p_y: Integer;
p_c_canvas_ref: tCanvas;
p_radius: Integer);
Procedure draw_shape; Override;
End; // c_ellipse_shape |
Le code de ces Classes et du projet principal sont très simples. Vous les trouverez dans le .ZIP du code source à télécharger
Pour illustrer cette librairie, voici un instantané après avoir déposé, déplacé, déposé, et déplacé:
2.5 - Le Diagramme de Classe
2.5.1 - Diagramme d'une Classe Le diagramme d'une Classe est un rectangle comportant trois compartiments - le nom de la Classe
- les attributs
- les méthodes
Pour notre Classe c_base_shape, il serait donc: Sur ce diagramme, vous distinguez aisément - le nom de la Classe (c_base_shape)
- les attributs (m_x, m_y, m_c_canvas_ref)
- les méthodes create_base_shape, draw, move_shape)
2.5.2 - Les versions enluminées
Notre diagramme est des plus épurés: pas de types de données, de paramètres etc La version officielle, normalisée, peut contenir toute une série de fioritures: - le nom d'une Classe abstraite est en italique
- les méthodes Virtual sont en italiques
- les attributs comportent leur type de données
- les paramètres des méthodes sont explicités
- les zones de sécurité (Private, Public etc) sont dotés de gris gris pour
les distinguer les unes des autres
- la notation distingue les Classes (c_rectangle) et des objets (mon_rectangle_18)
Une version plus complète serait, par exemple:
Pourquoi pas. Néanmoins les inconvénients sont assez évidents - la taille du dessin est largement augmentée. Donc si nous représentons 4 ou
5 Classes sur le même diagramme, soit nous défilons, soit nous rapetissons. Deux options qui sont nuisible à une compréhension globale de la situation. "Messieurs, de quoi s'agit-il ?", comme disait le maréchal Foch
- en plus la foison de détails masque les éléments importants. La présence, par exemple, d'un Constructor et d'un Destructor n'apporte pas grand chose. Nous savons bien qu'il faut appeler un Constructor pour allouer les
données d'une Classe et initialiser son type (sa VMT), et que si la Classe a alloué des données, il faudra les libérer.
- de façon similaires, les accesseurs (Set_xxx et Get_xxx) ne sont, même
dans la version UML officielle, en général pas représentés
Notre tendance serait plutôt à la simplification, pour retenir le diagramme suivant:
2.5.3 - Les Descendants Les deux descendants, c_rectangle_shape et c_ellipse_shape ont, éventuellement, par rapport à leur ancêtre c_basic_shape : - des attibrut supplémentaire
- des méthodes supplémentaire
- des méthodes ayant le même nom et des paramètres différents
- des éléments qui changent de zone de sécurité (de Private à Public, par exemple)
- du code implémentant certaines méthodes
Un descendant peut avoir toutes ces améliorations. Il en a en général au moins une, sinon cela ne servirait à rien de créer des descendants. Voici, par exemple, le diagramme qui représente c_rectangle_shape:
et: - le fait que c_rectangle_shape descende de c_basic_shape est représenté par la flèche (avec une pointe fermée)
- seuls les éléments nouveaux du descendant sont indiqués. Il hérite, par définition de l'héritage, des attributs et méthodes de son ancêtre
2.5.4 - La liste de c_shape
Le lien de 1 à N entre c_shape_list et c_basic_shape est présenté ainsi: Notez que : - le lien 1 à N est présenté avec un losange, côté conteneur
- sur la version UML "officielle", nous pouvons optionnellement spécifier les multiplicités (par exemple 1 à 3)
- il existe aussi une distinction entre la composition (une relation
impérative: une voiture DOIT avoir 4 roues) et l'aggrégation (un système informatique PEUT avoir une imprimante)
2.5.5 - Le diagramme complet Voici le diagramme complet
En résumé - chaque Classe contient les attributs et les méthodes clés de la Classe
- le diagramme complet modélise les relations entre les Classe. C'est à ce niveau que ces diagrammes sont fondammentaux : le code est linéaire, alors que les diagrammes sont à deux dimensions. La différence entre "la deuxième
à droite et la première à gauche" par rapport à Google Maps !
3 - Notes et Commentaires 3.1 - Techniques de Programmation Orientée Objet
Au niveau du code, nous avons utilisé directement les techniques de programmation objet. Il n'y a rien que du classique dans notre code: - des méthodes Virtual Abstract dans la figure ancêtre
- un conteneur utilisant un Array Of dynamique, avec une allocation buddy buddy (doublement de la taille lorsque le maximum est atteint)
- libération de la mémoire dans le conteneur c_shape_list
- absence de Constructor ou Destructor lorsque les méthodes de l'ancêtre suffisent
- méthode add_shape utilisant la Classe ancêtre c_basic_shape, et dans le code
- le paramètre est l'un des descendants (règle de compatibilité d'affectation)
- ce paramètre est fourni directement comme résultat de l'appel du Constructor
- utilisation de Is et du surtypage pour accéder aux propriétés d'un descendant
- séparation bien définie entre la Classe représentant la structure et celles représentant les élément (Bertrand MEYER)
Pour ceux qui ne seraint pas familiers avec ces techniques, voyez
3.2 - Diagrammes Personnalisés Nous nous sommes permis quelques libertés par rapport à la norme UML: - nous avons introduit la couleur, pour visualiser la différence de nature
entre les Classe. Ainsi, le conteneur c_shape_list ne joue pas le même rôle que c_rectangle_shape ou c_ellipse_shape. Les couleurs que nous utilisons n'ont aucune signification particulière (tous les conteneurs ne
sont pas vert, les éléments jaune etc). Ils permettent simplement de mieux visualiser les groupes de Classes
- nous visualisons le tableau en ajoutant les crochets "[ ]"
- de même nos méthodes sont dotées de parenthèses "()"
- si nous estimons important de fournir des informations sur un ou plusieurs paramètres, nous ajoutons son Type entre les parenthèses
3.3 - Utilisation comme outil de conception
Nous utilisons les diagrammes de classe UML au niveau de l'analyse et la conception. Le diagramme n'a d'ailleurs pas la même signification au moment de l'analyse (concepts au
niveau du problème de l'utilisateur : gérer des graphiques) et la conception (concepts au niveau de l'implémentation : utiliser un Array, une tObjectList ou une tCollection).
Nous pourrions aussi analyser sur un diagramme de Classe s'il faut placer le tCanvas dans chaque c_shape, ou le passer en paramètre chaque fois qu'il faut dessiner (draw_shape et move_shape)
Bien plus important, ils permettent aussi d'examiner plusieurs structures alternatives. Nous aurions pu utiliser, par exemple: - une organisation où le conteneur descend de l'objet abstrait, et utilise à
ce moment les mêmes méthodes (draw_shape et move_shape, mais aussi, par exemple print, save, load etc)
- ou encore une organisation où la liste est placée au niveau de la figure de base:
Cette structure permet les "listes de listes", et les figures peuvent contenir d'autres figures:
Le déplacement de la déclaration de la liste permet une structure de conteneurs récursive (figure de droite), bien plus riche que la simple liste (figure de gauche)
3.4 - Les Design Patterns En fait nous entrons là peu à peu dans le domaine des design patterns. Pour faire bref, les Design Patterns présentent un catalogue d'"organisation
type" de classes destinées à résoudre certaines catégories de problèmes L'exemple précédent est, par exemple, un pattern "composite"
3.5 - Notre utilisation des diagrammes de classe UML 3.5.1 - Présentation des articles
Voici, par exemple, le diagramme de classe qui nous avons utilisé dans l'article sur l'environnement de test unitaire
3.6 - Présentation de Classes Delphi Nous utilisons aussi des diagrammes de Classe dans nos transparents. Par exemple, dans la formation Base de
Données Multi Tiers , pour illustrer certaines facettes du tClientDataset de DataSnap, nous avons - la gestion des modifications en mémoire
- la sauvegarde et le chargement
- la mise à jour de Serveur SQL
Naturellement - ces diagrammes sont repris au besoin par des descriptions détaillées des attributs et des méthodes
- lorsque plusieurs Classes collaborent, les diagrammes reprennent ces Classes (et pas une seule Classe)
3.6.1 - Exploration de librairies
Tout développeur sait que prendre en main une librairie créée par un autre développeur nécessite un investissement non négligeable. De plus en plus, nous utilisons les Diagrammes de Classe comme un outil
d'exploration. Par exemple, voici le diagramme que nous avons établi pour représenter les tClientSocket et tServerSocket Delphi:
Nous avions déjà pratiqué les sockets au niveau de la librairie WinSockets, mais l'organisation Delphi n'était pas triviale. Sur le diagramme ci-dessus,
vous pouvez voir, entre autres, que les composants de la Palette ne sont que de minces encapsulations du véritable tWindowsSocket qui effectue le véritable travail (Connect, Accept etc)
Nous utilisons la même technique au niveau de nos missions client, pour explorer ou documenter les librairies existantes. Pour les librairies non triviales, nous ne commençons à toucher au code qu'après avoir dressé un
diagramme de classe des principales parties de la librairie.
3.6.2 - Projets plus complexes Pour des applications plus complexes, l'utilisation de diagrammes de Classe devient quasiment impérative.
Voici un IDE générique comportant - une zone de dessin (designer)
- une Palette (très primitive dans cette version)
- un Inspecter d'Objets
- un tTreeView
Cet IDE est générique dans la mesure où - nous pouvons l'incorporer à n'importe quelle application (il suffit d'indiquer quel conteneur, tel qu'un tPanel ou tTabSheet, doit accueillir l'IDE
- il suffit de créer les descendants de l'objet de base correspondant aux objets à utiliser dans cet IDE (des composants, des pièces, des traitements, des éléments graphiques etc)
Vous imaginez sans peine les interactions entre les différentes parties: - tout changement de positionnement d'une figure doit être synchronisée avec l'Inspecteur d'Objets
- la hiérarchie des figures (qui fait partie de qui) doit être synchronisée entre le designer et le tTreeView
- les changements de nom de l'Inspecteur d'Objet doivent être propagés dans le tTreeview
Lors de la première écriture, ces diagrammes pouvaient être omis. Nous avions tout en tête. Mais lorsque nous avons du maintenir le code (ajouts, modifications, corrections d'erreur), ces diagrammes sont devenus obligatoires
3.7 - Le graal du développeur: la génération de code Pour beaucoup, les diagrammes de classe sont un outil intermédiaire utilisé pour générer le code Delphi (ou Java, C++, C# etc). Ce n'est pas la voie que nous avons adopté:
3.8 - Les Diagrammes de Classe : une panacée ? Naturellement les diagrammes de classe UML ne sont pas le remède à tous nos maux.
Par définition, il ne représentent que l'aspect structurel et statique de nos Classes. L'aspect algorithmique est totalement absent de ces graphiques. Les diagrammes de séquence, ou encore les diagrammes d'état tentent de
présenter cet aspect du développement
3.9 - Diagrammes de Classe UML UML comporte donc d'autres diagrammes. Au moins 13. Mais il semblerait que les diagrammes de Classe se taillent la part du lion.
Sur 100 diagrammes, 90 sont en général des diagrammes de classe. Naturellement cela dépend du domaine et de ce que le développeur a décidé d'utiliser.
3.10 - Diagramme de Classe et Bases de Données
Mentionnons aussi que les diagrammes de Classe ont une certaine parenté avec les diagrammes Entité / Relation utilisé par les concepteurs de bases de données. Les chercheurs dans ces domaines étaient bien en avance sur la programmation
objet, et le restent à bien des égards. Néanmoins les diagrammes ER sont un peu différents: - ils ne modélisent que les données (il n'y a pas de méthodes)
- ils ne sont concernés que par les liaisons entre tables (clés primaires et
secondaires). Il n'y a pas de concept d'héritage
3.11 - UML et Delphi 3.11.1 - Utilisation d'UML avec Delphi Lorsque Delphi a incorporé ECO, et par conséquent l'outil UML Together, il y a
eu une série d'articles expliquant ou utilisant les diagrammes UML. Néanmoins, la littérature officielle (y compris la documentation Delphi) utilisent peu les diagrammes de Classe. Le monde Java ou C# en est beaucoup
plus friand, et nombreux sont les articles C# contenant un diagramme de Classe (souvent pour analyser une Classe de la librairie, mais aussi pour présenter les classes proposées par l'article). Il semble que les développeurs
Java et C# trouvent leur environnement Eclipse ou Visual Studio plus propice à l'utilisation d'UML
3.11.2 - Les outils UML Delphi Pourtant ce ne sont pas les outils UML qui font défaut.
A part quelques incursions dans les outils mentionnés ci-dessus, nous utilisons
pour notre part notre éditeur vectoriel graphique, que nous avons adapté pour UML. L'utilisation d'outils spécifiques présente comme d'habitude des inconvénients et des avantages
- ils sont en général moins complets, et moins "fignolés" que les outils commerciaux
- ils permettent une adaptation personnalisé. Citons, dans notre cas
- un mécanisme de "zoom" qui permet de regrouper plusieurs Classes en un
groupe de Classe (mieux intégré que les Package UML, et similaire aux conteneurs de BON)
- des sauvegardes simples dans des fichiers .TXT (une structure similaire au .DFM)
- une saisie bien plus rapide (frappe dans un tMemo pour toute la Classe, au lieu d'avoir à cliquer, taper, Entrée etc pour chaque attribut ou méthode, possibilité d'organiser ses lignes, de ne pas préciser le type etc)
- la possibilité d'ajouter des couleurs
Outre la création de programmes, certains outils savent aussi effectuer le trajet inverse: construire automatiquement les diagrammes correspondant aux Classes d'un projet.
Dans cette catégorie d'utilitaire, citons: - EssModel dont voici la sortie sur notre mini librairie:
- Together sait aussi construire un diagramme de classe à partir de source. Voici le résultat sur notre projet
Nous remarquerons que
- les diagrammes sont isolés par unité
- le tTreeview sur la droite est assez complexe
- nous avons publié sur nos sites de nombreux articles avec des analyseurs
syntaxiques Delphi, et ceux-ci peuvent être facilement adaptés pour extraire les informations des sources. Reste la partie graphique, que nous n'avons, effectivement, pas publiée à ce jour
4 - Télécharger le code source Delphi Vous pouvez télécharger:
Ce .ZIP qui comprend: - le .DPR, la forme principale, les formes annexes eventuelles
- les fichiers de paramètres (le schéma et le batch de création)
- dans chaque .ZIP, toutes les librairies nécessaires à chaque projet (chaque
.ZIP est autonome)
Ces .ZIP, pour les projets en Delphi 6, contiennent des chemins RELATIFS. Par conséquent: - créez un répertoire n'importe où sur votre machine
- placez le .ZIP dans ce répertoire
- dézippez et les sous-répertoires nécessaires seront créés
- compilez et exécutez
Ces .ZIP ne modifient pas votre PC (pas de changement de la Base de Registre, de DLL ou autre). Pour supprimer le projet, effacez le répertoire.
La notation utilisée est la notation alsacienne qui consiste à préfixer les identificateurs par la zone de compilation: K_onstant, T_ype, G_lobal,
L_ocal, P_arametre, F_unction, C_lasse. Elle est présentée plus en détail dans l'article La
Notation Alsacienne
Comme d'habitude: - nous vous remercions de nous signaler toute erreur, inexactitude ou
problème de téléchargement en envoyant un e-mail à jcolibri@jcolibri.com. Les corrections qui en résulteront pourront aider les prochains lecteurs
- tous vos commentaires, remarques, questions, critiques, suggestion d'article, ou mentions d'autres sources sur le même sujet seront de même les bienvenus à jcolibri@jcolibri.com.
- plus simplement, vous pouvez taper (anonymement ou en fournissant votre e-mail pour une réponse) vos commentaires ci-dessus et nous les envoyer en cliquant "envoyer" :
- et si vous avez apprécié cet article, faites connaître notre site, ajoutez un lien dans vos listes de liens ou citez-nous dans vos
blogs ou réponses sur les messageries. C'est très simple: plus nous aurons de visiteurs et de références Google, plus nous écrirons d'articles.
5 - Références Voici quelques liens utiles :
- UML 2 : Guide de rérérence - le livre de référence UML de James Rumbaugh, Ivar
Jacobson, et Grady Booch - 800 pages
- EssModel l'outil Ess Model de Eldean
- nos articles sur la programmation objet Delphi
- c_list_of_double encapsulation de liste de
valeurs réelles, en utilisant des ARRAY OF Double, des tList de ^Double et des tList de c_double. Ainsi que les listes de plusieurs réels, comme par exemple des points (x, y, z)
- Constructeur Delphi Virtuels : les Constructeurs Virtuels et les Références de classes (VIRTUAL
CONSTRUCTORS, CLASS OF) permettent la séparation entre un projet et son fichier .EXE et des modules compilés séparément et liés à l'exécution. Par conséquent le point de départ pour les Framework Applicatifs et les Plugins
- nos articles sur UML
- l'éditeur de document LEXI présenté comme
étude de cas du livre GOF. Les patterns Composite, Decorateur, Itérateur, Visiteur, Stratégie, Commande, avec explications, diagrammes UML, et sources Delphi complets
- les design patterns Abstract Factory et Bridge. Présentation, diagrammes de classe UML et sources
Delphi.
- Les Design Patterns standards le codage en Delphi des 23 Design Patterns du livre des "Gang Of Four"
- UML avec Delphi : comment créer des diagrammes UML avec Delphi et Together: diagramme de classe, cas d'utilisation et diagramme de séquence. Avec
génération de code et synchronisation entre le code Pascal et les diagrammes
- quelques exemples de diagrammes de classe accompagnant des articles sur ce site, ou des formations que nous proposons (utilisés pour illustrer le texte ci-dessus)
- et, nous proposons aussi des
formations présentant les techniques objet ou UML
6 - L'auteur John COLIBRI est passionné par le développement Delphi et les applications de Bases de Données. Il a écrit de nombreux livres et articles, et partage son temps entre le développement de projets (nouveaux projets, maintenance, audit, migration BDE, migration Xe_n, refactoring) pour ses clients, le
conseil (composants, architecture, test) et la
formation. Son site contient des articles
avec code source, ainsi que le programme et le calendrier des stages de formation Delphi, base de données, programmation objet, Services Web, Tcp/Ip et
UML qu'il anime personellement tous les mois, à Paris, en province ou sur site client. |