+ 33 6 68 40 27 75 contact@odeven.fr

Comprendre les Concepts Objets – Introduction à UML : Diagrammes de classe

par | Sep 2, 2022 | Concepts Objets

Sommaire

Programmation Orientée Objet, Design/Architectural Patterns, … Vous en avez déjà entendu parler. Mais savez vous précisément ce en quoi ça consiste ?

Ces 8 articles vous permettront de comprendre les grands principes qui se cachent derrière ces termes techniques, mais également d’apprendre comment et pourquoi les utiliser.

Qu’est-ce que l’UML ?

UML, signifiant “Unified Modeling Language”, est un langage, non pas de programmation, mais de modélisation graphique. Ce langage consiste en un ensemble de diagrammes permettant de représenter schématiquement l’architecture, les processus, les fonctionnalités, etc… d’une application.

Ce langage est actuellement le plus adapté dans son domaine, concernant la majeure partie des cas. Il est parfois associé à certains diagrammes du langage “Merise” (qui ne sera pas abordé), encore quelques fois utilisé.

Les diagrammes UML sont nombreux (diagramme de classe, diagramme de cas d’utilisation, diagramme d’activité, …). Seul le diagramme de classe sera détaillé ici.

Quel est l’intérêt de l’UML ?

Dans un projet, vous serez probablement amené à travailler en équipe. Dans ce cas, il sera essentiel de définir clairement le fonctionnement ou l’architecture de votre application. Une grosse application n’est pas toujours évidente à comprendre en se basant uniquement sur le code, même pour un développeur.

Si vous êtes seul, l’UML est une très bonne façon pour vous de réfléchir à la manière dont vous allez développer votre projet, en vous assurant d’avoir réfléchi à l’application ou à une fonctionnalité dans son intégralité.

Pour les clients, ces diagrammes sont généralement simples à comprendre. Et même si un client ne connaît pas l’UML, il sera capable de comprendre une grande partie de vos diagrammes.

Enfin, dans un soucis de documentation, l’UML constitu une base solide.

Qu’est-ce qu’un diagramme de classe ?

Il s’agit du diagramme le plus utilisé, car il permet de représenter de manière très concrète l’architecture de votre application, les classes qui la composent, et les liens entre ces dernières. Comme son nom l’indique, un diagramme de classe se base sur les classes, et donc sur la Programmation Orientée Objet. En effet, ce diagramme permet de représenter schématiquement une classe, ses attributs, ses méthodes, la visibilité de ceux-ci, et les relations telles que l’héritage, les compositions, etc…

En réalité, on utilise parfois ce diagramme pour modéliser une base de données relationnelle.

Les classes

Une classe est composée de trois parties :

  • Son nom
  • Ses attributs
  • Ses méthodes

Ainsi, une classe en UML est représentée par un rectangle, composé de ces trois parties : le nom, puis les attributs, et enfin les méthodes.

Le nom

Basé sur les mêmes standards de nommage que la Programmation Orientée Objet, il peut néanmoins être représenté de différentes manières.

S’il s’agit d’une classe abstraite, le nom sera écrit en italique.

S’il s’agit d’une interface, on ajoute le mot clef <<interface>>, juste au dessus du nom.

Règle de nommage pour une classe, une classe abstraite, et une interface

Les attributs

En UML, comme en Programmation Orientée Objet, un attribut se définit en trois parties : un nom, un type, et une visibilité. Il sera généralement représenté de la façon suivante :

visibilité nomAttribut : type

Exemple :

Représentation des attributs en UML

Nom

Le nom de l’attribut doit respecter les conventions de nommage en vigueur.

Type

Le type peut-être scalaire (int, float, char, etc…), ou bien objet (String, DateTime, etc…).

Visibilité

La visibilité n’utilise pas la même syntaxe que la programmation, on utilisera plutôt un symbole au lieu des mots clés “public”, “private”, etc… Voici ces symboles :

+ : public
- : private
# : protected
~ : package

Il est possible de définir des informations complémentaires, telles que la multiplicité de l’attribut (s’il existe plusieurs fois) ou bien sa valeur par défaut. Voici une manière plus complète, bien que moins souvent utilisée, de représenter un attribut en UML :

visibilité nomAttribut[multiplicité] : type = valeurParDefaut

Exemple :

+ numTelephones[3] : string = ‘00 00 00 00 00’

Les méthodes

Une méthode se définit d’une manière semblable à un attribut. On écrira d’abord la visibilité de la méthode, suivi du nom de la méthode, puis des arguments en paramètre, et enfin le type de retour de la méthode si celle-ci retourne quelque chose :

visibilité nomMethode(nomArg1 : typeArg1, nomArg2 : typeArg2) : typeRetour

Exemple :

Représentation des méthodes en UML

Nom

Le nom de la méthode doit respecter les conventions de nommage en vigueur.

Arguments

Les arguments sont décrits entre les parenthèses, comme en programmation. Le nom des arguments doit respecter les conventions de nommage en vigueur. Chaque argument est séparé par une virgule. Le nom d’un argument est suivi de “:” puis de son type.

Type de retour

Le type de retour est bien évidemment optionnel si la méthode ne retourne rien. Il peut-être scalaire (int, float, char, etc…), ou bien objet (String, DateTime, etc…).

Visibilité

La même règle que pour la visibilité des attributs est utilisée pour les méthodes, à savoir les 4 symboles “+”, “”, “#”, et “~”.

Les types de relations

Héritage

L’héritage en UML est représenté par une flèche, dont l’extrémité est représentée par un triangle pointant la classe “parent”.

Exemple d’héritage

La représentation de l’héritage peut-être simplifiée visuellement si plusieurs classes “enfant” étendent la même classe “parent”.

Simplification de la représentation de l’héritage

Association

Une association est un lien entre deux classes. En programmation, une association est représentée par un attribut dans une classe qui est du type d’une autre classe. Par exemple, dans un service de support par téléphone, un agent du support peut être lié à un client, ou peut-être même plusieurs. On ajouterait donc un attribut “clients” dans la classes “Agent”. On pourrait même avoir un attribut “agent” dans la classe “Client”. On pourrait représenter cet exemple de la manière suivante : 

Exemple d’une association d’un point de vue “code”

L’exemple ci-dessus n’est pas bon d’un point de vue UML. En effet, les associations ne doivent pas être représentées avec des attributs, mais par des traits, qui relient les classes associées :

Représentation d’une association en UML

Une relation peut aussi être nommée, c’est-à-dire qu’on définit le nom des attributs de chaque côté de la relation. D’un point de vue “code”, on aurait un attribut “clients” dans la classe “Agent”, et un attribut “agent” dans la classe “Client”. Il n’est pas souvent utile de nommer les relations, car cela alourdit fortement le diagramme. Il faut donc s’en servir uniquement lorsque cela est nécessaire pour comprendre le “rôle” par exemple qu’une classe joue par rapport à une autre. De plus, un nom peut-être définit au milieu du trait, afin de préciser le rapport entre les deux classes. On aurait pu ajouter par exemple “traite les demandes de”. Cela signifierait qu’un agent traite les demandes de clients.

Représentation d’une association nommée en UML

Les exemples ci-dessus sont “bidirectionnels”, c’est-à-dire que le lien est définit dans les deux sens (un attribut dans les deux classes). Mais dans certains cas, le lien n’est utile que dans un seul sens (de la classe Agent vers la classe Client par exemple). Dans ce cas, l’association sera représentée par une flèche dont l’extrémité pointe vers la classe cible, et sera dite “unidirectionnelle”.

Représentation d’une association unidirectionnelle en UML

Une autre informations importante peut-être ajoutée sur une association. Il s’agit de la multiplicité (ou cardinalité). Cela va permettre de définir le nombre d’instance entrant en jeu dans une association (OneToOne, OneToMany/ManyToOne, ManyToMany).

Dans l’exemple ci-dessus, nous avons déterminé qu’un agent pouvait gérer plusieurs clients. Mais un client ne peut, lui, être géré que par un seul agent. Dans ce cas, nous pouvons ajouter cette multiplicité sur l’association.

En UML, la multiplicité est représentée de la manière suivante :

Nombre d’instance précis

On écrit précisément le nombre d’instances en jeu. Exemple :

  • Une seule instance : 1
  • Deux instances : 2
  • Etc…

Nombre variable d’instances

On écrit le minimum et le maximum d’instances en jeu, séparés par deux point “..”. Exemple :

  • Entre 0 et 1 : 0..1
  • Entre 0 et l’infini : * ou juste 0..*
  • Au moins une instance (nullable = false) : 1..*
Représentation d’une association avec multiplicité en UML

Dans l’exemple ci-dessus, un agent a au moins 1 client, et peut en avoir une infinité. Et un client ne peut avoir qu’un seul agent. On lit toujours “1 objet A est lié à X objet(s) B”, le “X” étant le nombre écrit à côté de la classe B, et “1 objet B est lié à Y objet(s) A”, le “Y” étant le nombre écrit à côté de la classe A.

Agrégation

D’un point de vue code, une agrégation est exactement la même chose qu’une association. La seule différence réside dans la sémantique du lien qui relie les deux classes. En effet, l’UML n’a pas pour seul but de représenter visuellement du code, mais aussi la logique de l’application, le rôle que joue chaque classe, etc… Et même si certaines choses sont implémentées dans le code de la même manière, le sens peut varier suivant les cas.

Une agrégation ajoute donc un sens à une association. Il s’agira d’un lien plus fort que pour une simple association. Ce lien a pour but de définir qu’un objet contient d’autres objets. Dans l’exemple de l’association cité précédemment, un agent est un client sont liés par un lien, mais un client ne fait pas partie d’un agent, et réciproquement.

Prenons un exemple concret. Imaginons que nous disposons de salles, et que ces salles contiennent des tables. Ces tables, bien que pouvant être déplacées d’une salle à une autre, font partie d’une salle d’un point de vue sémantique. Le lien qui unira la classe “Salle” et la classe “Table” sera une agrégation.

Une agrégation est représentée en UML par un trait, dont une des extrémité est un losange vide, pointant vers la classe qui possède l’autre.

Représentation d’une agrégation  en UML

Composition

Une composition est exactement la même chose qu’une agrégation, à une exception près. Dans le cas d’une agrégation, la suppression de l’objet qui contient l’autre n’entraîne pas automatiquement la suppression du second. Dans le cas d’une composition, vous l’aurez compris, la suppression du premier entraîne la suppression du second. L’inverse n’est pas vrai.

D’un point de vue “code”, il y aurait éventuellement quelque part un algorithme qui procéderait à la suppression automatique du second objet.

D’un point de vue “base de données”, on définirait ce qu’on appel une “suppression en cascade”.

La représentation en UML d’une composition est la même que pour une agrégation, sauf que le losange doit être plein.

Représentation d’une composition en UML

Dans l’exemple ci-dessus, la suppression d’un utilisateur entraîne automatiquement la suppression de ses numéros de téléphone.

Dépendance

Une dépendance en UML permet de définir qu’une classe ne peut pas fonctionner sans une autre. Contrairement aux associations/agrégations/compositions, une dépendance d’un point de vue “code” ne correspond pas forcément à un attribut du type de la classe cible.

Prenons un exemple simple. Nous disposons d’une classe “Calculatrice”, permettant de faire des calculs complexes (prendre une expression scientifique, et déterminer le résultat, peu importe le nombre d’opérations qui la composent). Nous disposons aussi d’une classe “Math” permettant de faire des calculs basiques (addition, soustraction, etc…). La classe “Calculatrice” aura besoin de la classe “Math” pour fonctionner.

Si on supprime la classe “Math”, la classe “Calculatrice” ne pourra plus fonctionner. La classe “Math” est totalement indépendante, mais la classe “Calculatrice”, elle, dépend de la classe “Math” pour fonctionner.

Au niveau du code, on retrouvera généralement des appels à des méthodes statiques de la classe “Math” dans une/des méthode(s) de la classe “Calculatrice”, ou bien une injection de dépendance (ce concept sera détaillé dans un autre chapitre).

En UML, une dépendance est représentée par une flèche semblable à une association, mais avec des pointillés.

Représentation d’une dépendance en UML

Les packages

En général, un diagramme de classe représente une partie spécifique d’une application, comme une application, ou les classes d’un seul package, mais rarement l’application dans son intégralité. Imaginez un diagramme faisant figurer des centaines de classes, des milliers d’attributs et méthodes. Ce serait illisible. On préfère généralement faire plusieurs diagrammes de classe, représentant chacun une partie de l’application nécessitant d’être détaillée.

Les packages en programmation, ou bien namespaces (espaces de nom), peuvent être représentés sur un diagramme de classe. En voici un exemple :

Représentation des packages en UML

Le nom du package est parfois écrit dans le petit onglet situé en haut à gauche de chaque rectangle.

Chaque package peut bien sûr contenir des classes, et les classes qui y figurent peuvent interagir avec les classes d’autres packages. Exemple :

Exemple de packages avec des classes en UML

Il est aussi possible de faire des sous-packages (des packages dans des packages), comme en programmation.

Pour résumer

UML permet de mettre à plat l’analyse, de manière visuelle. Faire un diagramme de classe nécessite un minimum de rigueur, mais contribue à se faire comprendre rapidement, et de parler « le même langage ».

Il existe de nombreux diagrammes, chacun permettant de détailler d’une manière claire certains aspects de l’analyse.

UML : Langage de modélisation graphique permettant de schématiser une architecture, un fonctionnement, etc…

Classe : Composée d’un nom, d’attributs, et de méthodes.

Héritage : Une classe fille hérite des attributs/méthodes d’une classe mère.

Association : Lien faible entre deux classes.

Agrégation : Association avec notion de “composition”.

Composition: Agrégation avec notion de suppression en cascade.

Dépendance : Une classe peut dépendre d’une autre pour fonctionner.

Multiplicité /Cardinalité : Permet de définir le nombre d’instance qui relie deux classes.

Navigabilité : Permet de définir si le lien va dans un sens (unidirectionnel), ou dans les deux (bidirectionnel).

Package : Permet de définir les différentes parties d’une application.