menu
  Home  ==>  articles  ==>  uml_design_patterns  ==>  diagrammes_de_classe_uml   

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_yinteger;
           m_c_canvas_reftCanvas;

           Constructor create_base_shape(p_xp_yInteger;
               p_c_canvas_reftCanvas);
           Procedure draw_shapeVirtualAbstract;
           Procedure move_shape(p_delta_xp_delta_yInteger);
         End// c_base_shape



Voici la définition de la liste des figures:

Type c_shape_list=
         Class
           m_shape_arrayArray Of c_base_shape;
           m_shape_countinteger;

           Constructor create_shape_list;
           Procedure draw_shape_list;
           Procedure add_shape(p_c_base_shapec_base_shape);
           Procedure move_shape(p_delta_xp_delta_yInteger);
           Destructor DestroyOverride;
         End// c_shape_list



Et nos deux types de figures sont définies ainsi:

Type c_rectangle_shape =
         Class(c_base_shape)
           m_widthm_heightInteger;

           Constructor create_rectangle_shape(p_xp_yInteger;
               p_c_canvas_reftCanvas;
               p_widthp_heightInteger);
           Procedure draw_shapeOverride;
           Procedure resize(p_scaleInteger);
         End// c_rectangle_shape

     c_ellipse_shape =
         Class(c_base_shape)
           m_radiusInteger;

           Constructor create_ellipse_shape(p_xp_yInteger;
               p_c_canvas_reftCanvas;
               p_radiusInteger);
           Procedure draw_shapeOverride;
         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é:

graphic_shape_library



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:

class_diagram

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:

class_diagram_with_types

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:

simple_class_diagram



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:

inheritance_class_diagram

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:

composition_class_diagram

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

shape_library_class_diagram

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)

    composite_design_pattern

  • ou encore une organisation où la liste est placée au niveau de la figure de base:

    list_of_lists

    Cette structure permet les "listes de listes", et les figures peuvent contenir d'autres figures:

    list__vs__composite

    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

actual_unit_test_example_uml_class_diagram



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

    clientdataset_changelog

  • la sauvegarde et le chargement

    clientdataset_load_save

  • la mise à jour de Serveur SQL

    clientdataset_applyupdates

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:

delphi_sockets_architecture

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
generic_ide

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é:

  • nos diagrammes de classe ont pour vocation essentielle de guider la réflexion au moment de l'analyse et la conception. Ils sont donc partiels et incomplets
  • pour générer du code, il faut avoir les informations suffisantes pour le faire. Par exemple, pour générer

    Type ma_classe=
           Class
             total : Integer;
           End

    il faut bien que le diagramme contienne l'information "Integer" quelque part. Soit explicitement, soit par une convention de notation quelconque (%total pour un entier, $total pour une string etc)

  • cette contrainte syntaxique occupe notre esprit pendant l'analyse / conception, alors que lors de ces phases, nous devrions nous concentrer sur les responsabilités de chaque Classe et ses relations avec les autres Classes
  • naturellement, notre outil UML (utilisé pour les diagrammes ci-dessus) est capable de générer
    • soit les classes Delphi complètes si nous avons fourni (dans une étape juste avant le code) les détail syntaxiques
    • soit des classes "partielles" (des attributs sans type, des méthodes sans aucun paramètre)
    Mais ce n'est pas pour nous le point essentiel


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.
  • historiquement, le premier outil a été Model Maker. Cet outil offrait
    • la modélisation UML
    • la génération de code
    • les Design Patterns
    • la synchronisation entre le modèle et le code
    En fait le code est représenté par une structure interne, qui est visualisée par des diagrammes UML, et traduite par le code Pascal.
  • Sparx System, contemporain de Model Maker offre juste la génération de code Delphi. Sans trop d'intérêt donc
  • comme mentionné, Delphi a incorporé vers 2006 ECO, et a acheté à ce moment l'outil Together. Nous avons écrit un article expliquant comment utiliser Together avec Delphi

    Voici un exemple:

    delphi_together

    Cet outil est assez complet. Il permet

    • la génération du code
    • l'analyse reverse engineering (vous fournissez le code et il produit le diagramme de classe correspondant)
    Néanmoins, Together nous semble un peu lourd et touffu pour une utilisation en analyse / conception

  • si la génération de code n'est pas importante (et elle ne devrait pas être prioritaire), il est possible d'utiliser
    • Argo UML qui est un outil en source
    • n'importe quel autre outil capable de dessiner des rectangles avec du texte est des liens. Certains de nos clients dans le domaine de l'automatisme utilisent ainsi Grafcet. D'autres emploient Visio
    • des outils maison.


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:

    ess_model

  • Together sait aussi construire un diagramme de classe à partir de source. Voici le résultat sur notre projet

    together_reverse_engineering

    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" :
    Nom :
    E-mail :
    Commentaires * :
     

  • 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 :


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.
Created: jan-04. Last updated: mar-2020 - 250 articles, 620 .ZIP sources, 3303 figures
Contact : John COLIBRI - Tel: 01.42.83.69.36 / 06.87.88.23.91 - email:jcolibri@jcolibri.com
Copyright © J.Colibri   http://www.jcolibri.com - 2001 - 2020
Retour:  Home  Articles  Formations  Développement Delphi  Livres  Pascalissime  Liens  Download
l'Institut Pascal

John COLIBRI

+ Home
  + articles_avec_sources
    + bases_de_donnees
    + web_internet_sockets
    + services_web_
    + prog_objet_composants
    + office_com_automation
    + colibri_utilities
    + uml_design_patterns
      – gof_design_pattern
      – uml_avec_delphi
      – diagramme_de_classe
    + graphique
    + delphi
    + outils
    + firemonkey
    + vcl_rtl
    + colibri_helpers
    + colibri_skelettons
    + admin
  + formations
  + developpement_delphi
  + présentations
  + pascalissime
  + livres
  + entre_nous
  – télécharger

contacts
plan_du_site
– chercher :

RSS feed  
Blog

Migration Delphi migration de versions Delphi, migration Unicode, migration BDE / base de données, migration Internet - Tél 01.42.83.69.36
Formation Delphi xe3 complete L'outil de développpement, le langage de programmation, les composants, les bases de données et la programmation Internet - 5 jours