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.
- La Programmation Orientée Objet
- Introduction à UML : Diagrammes de classe
- Concepts de base de POO
- Les Patrons de Conception (Design Patterns)
- Les Patrons d’Architecture (Architectural Patterns)
- Concepts avancés de POO
- Qualité de Code
- Web Services
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.
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 :
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 :
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”.
La représentation de l’héritage peut-être simplifiée visuellement si plusieurs classes “enfant” étendent la même classe “parent”.
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 :
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 :
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.
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”.
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..*
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.
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.
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.
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 :
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 :
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.