Les réalisations de cas d’utilisation (RDCU) sont le sujet du chapitre F17/A18 . Voici les points importants pour la méthodologie :
- Une RDCU est une synthèse des informations spécifiées dans le MDD, le DSS et les contrats d’opération. Elle sert à esquisser une solution (qui n’est pas encore codée) afin de rendre plus explicite l’activité impliquant des choix de conception. Si vous n’avez pas bien compris les autres éléments (MDD, DSS, etc.), il vous sera difficile de réussir les RDCU. Il est normal de ne pas tout comprendre au début, alors posez des questions si vous ne comprenez pas.
- De manière générale, toute bonne RDCU doit faire les choses suivantes :
- spécifier un contrôleur (pour la première opération système dans un DSS, qui sera le même pour le reste des opérations dans le DSS) ;
- satisfaire les postconditions du contrat d’opération correspondant ;
- faire usage des classes conceptuelles du MDD et respecter leurs associations et cardinalités ;
- rechercher les informations qui sont éventuellement rendues à l’acteur dans le DSS.
- Il s’agit d’un diagramme de séquence en UML. Il faut alors maîtriser la notation UML pour ces diagrammes, mais on applique la notation de manière agile :
- Il n’est pas nécessaire de faire les boîtes d’activation, car ça prend du temps de les faire correctement lorsqu’on dessine à la main un diagramme ;
- On doit se servir des annotations pour documenter les choix (GRASP) ;
- On dessine à la main des diagrammes puisqu’on peut faire ça en équipe sur un tableau blanc, mais aussi, à l’examen, vous devez faire des diagrammes à la main ;
- Au lieu d’un message pointillé indiquant le retour d’une valeur à la fin de l’exécution d’une méthode, on utilise l’affectation sur le message (comme dans la programmation), par exemple
c = getClient(...)
à la figure 9.4.
- Le livre de Larman (2005) présente quelques RDCU qui sont des diagrammes de communication. Cette notation n’est pas utilisée dans ce manuel, car elle est plus complexe à utiliser et elle est comparable à la notation des diagrammes de séquence.
- Faire des RDCU est plus agile que coder, car, dans un diagramme, on peut voir le flux de plusieurs messages à travers plusieurs classes. Dans une solution codée, il serait nécessaire d’ouvrir plusieurs fichiers afin de voir le code de chaque méthode (message), et l’on ne peut pas voir toute la dynamique de la même manière. Faire des changements à un diagramme (avant de le coder) est en principe plus facile que de changer le code source. On peut également se servir des structures (
List
,Array
,Map
, etc.) dans un diagramme, avant que celles-ci ne soient créées. - Faire des RDCU est une activité créative. Un diagramme dynamique en UML peut avoir une mauvaise logique, car il s’agit d’un dessin. Le codage dans un langage de programmation est la seule manière de valider une RDCU. Évidemment, la programmation prend beaucoup plus de temps et n’est pas insignifiante. Faire une RDCU est comme faire un plan pour un bâtiment, tandis que faire de la programmation est comme construire le bâtiment. Si un plan contient des erreurs de conception, on va les connaître lors de la construction. Alors, votre RDCU sera incertaine jusqu’à ce que vous la traduisiez en code exécuté et testé.
Tout le processus de proposition d’une solution (RDCU) peut être visualisé comme un diagramme d’activités, comme illustré sur la figure 9.1.
9.1 Spécifier le contrôleur
Pour commencer une RDCU, on spécifie le contrôleur selon GRASP. Dans les travaux réalisés selon la méthodologie de ce manuel, vous devez indiquer pourquoi vous avez choisi telle classe pour être le contrôleur. Ce n’est pas un choix arbitraire. Référez-vous à la définition dans le tableau 6.1.
Pour initialiser les liens entre la couche présentation et les contrôleurs GRASP, Larman vous propose de le faire dans la RDCU pour l’initialisation, le scénario Démarrer.
9.2 Satisfaire les postconditions
9.2.1 Créer une instance
Certaines postconditions concernent la création d’une instance. Dans votre RDCU, vous devez respecter le GRASP Créateur, selon la définition dans le tableau 6.1.
9.2.2 Former une association
Pour les postconditions où il faut former une association entre un objet a et un objet b, il y a plusieurs façons de faire.
- S’il y a une agrégation entre les objets, il s’agit probablement d’une méthode
add()
sur l’objet qui agrège. - S’il y a une association simple, il faut considérer la navigabilité de l’association. Est-ce qu’il faut pouvoir retrouver l’objet a à partir de l’objet b, ou vice-versa ? Il s’agira d’une méthode
setB(b)
sur l’objeta
(pour trouver b à partir de a), etc. - S’il faut former une association entre un objet et un autre « sur une base de correspondance avec » ou « correspondant à » un identifiant passé comme argument (voir les postconditions de l’exemple à la section Section 8.3), alors il faut repérer le bon objet d’abord. Voir la section Transformer identifiants en objets.
Dans la plupart des cas, la justification pour former une association est GRASP Expert, défini dans le tableau 6.1. Il faut faire attention à la visibilité .
9.2.3 Modifier un attribut
Pour les postconditions où il faut modifier un attribut, c’est assez évident. Il suffit de suivre le principe GRASP Expert, défini dans le tableau 6.1. Très souvent, c’est une méthode setX(valeur)
, où X
correspond à l’attribut qui sera modifié à valeur
. Attention à la visibilité .
Lorsque l’attribut d’un objet doit être modifié juste après la création de ce dernier, ça peut se faire dans le constructeur, comme illustré sur la figure 9.2.
9.3 Visibilité
Dans une approche orientée objet, puisqu’on doit éviter trop de couplage, un objet ne voit pas tous les autres objets. Si un objet a veut envoyer un message à un objet b, ce dernier doit lui être visible. Ça veut dire que a doit connaître une référence vers b.
Régler les problèmes de visibilité lorsqu’on crée une RDCU nécessite de la créativité. Il faut pratiquer pour apprendre la démarche, mais les points suivants peuvent aider :
- Pour un objet racine (par exemple
Université
), il peut s’agir d’un objet Singleton, qui aura une visibilité globale, c’est-à-dire que n’importe quel objet pourrait lui envoyer un message. Cependant, les objets Singleton posent des problèmes de conception, notamment pour les tests. Il vaut mieux éviter ce choix, si possible.
Voir cette réponse sur Stack Overflow . - Sinon, il faudra que l’objet émetteur (a) ait une référence de l’objet récepteur (b). Par exemple, sur la figure 9.3, la référence à b peut être :
- stockée comme un attribut de a,
- passée comme un argument dans un message antérieur, ou
- affectée dans une variable locale de la méthode où
unMessage()
sera envoyé.
Pour plus de détails, voir le chapitre sur la Visibilité (F18/A19) .
Pour initialiser les références nécessaires pour la bonne visibilité, Larman vous propose de faire ça dans la RDCU pour l’initialisation, le scénario Démarrer.
9.4 Transformer les identifiants en objets
La directive d’utiliser les types primitifs pour les arguments dans les opérations système (voir la Chapitre 5) nous mène à un problème récurrent dans les RDCU : transformer un identifiant (un argument de type String
ou int
) en objet représenté par cet identifiant. Larman vous propose un idiome (pas vraiment un pattern) nommé Transformer identifiant en objet (fin de la section F23.8, p.451/A26.8, IDs to Objects ), qui sert à repérer l’objet qui correspond à l’identifiant.
Il y a un exemple à la figure 9.4 provenant du chapitre sur l’Application des patterns GoF (figure F23.18 ). Un autre exemple du livre de Larman (2005) est l’identifiant codeArticle
transformé en objet DescriptionProduit
par la méthode
CatalogueProduits.getDescProduit(codeArticle:String):DescriptionProduit
.
La Section 9.5 explique comment implémenter la transformation avec un tableau associatif.
9.5 Utilisation d’un tableau associatif (Map<clé, objet>
)
Pour transformer un identifiant en objet, il est pratique d’utiliser un tableau associatif (aussi appelé dictionnaire ou map en anglais) . L’exemple du livre de Larman (2005) concerne le problème de repérer une Case
Monopoly à partir de son nom (String
). C’est illustré sur la figure F16.7/A17.7 .
Notez que les exemples de Larman (2005) ne montrent qu’un seul type dans le tableau associatif, par exemple Map<Case>
, tandis que, normalement, il faut spécifier aussi le type de la clé, par exemple Map<String, Case>
.
Un tableau associatif fournit une méthode get
ou find
pour rechercher un objet à partir de sa clé (son identifiant). La figure 9.5 en est un exemple.
Dans la section suivante, l’initialisation des éléments utilisés dans les RDCU (comme des tableaux associatifs) est expliquée.
9.6 RDCU pour l’initialisation, le scénario Démarrer
Le lancement de l’application correspond à la RDCU « Démarrer ». La section Initialisation et cas d’utilisation Démarrer (F17.4 , p. 345) ou Initialization and the Start Up Use Case (A18.4 , p.274) traite ce sujet important. C’est dans cette conception où il faut mettre en place tous les éléments importants pour les hypothèses faites dans les autres RDCU, par exemple les classes de collection (map), les références pour la visibilité, l’initialisation des contrôleurs, etc.
Voici quelques points importants :
- Le lancement d’une application dépend du langage de programmation et du système d’exploitation.
- À chaque nouvelle RDCU, on doit possiblement actualiser la RDCU « Démarrer » pour tenir compte des hypothèses faites dans la dernière RDCU. Elle est assez « instable » pour cette raison. Larman recommande de faire sa conception en dernier lieu.
- Il faut choisir l’objet du domaine initial, qui est souvent l’objet racine, mais ça dépend du domaine. Cet objet aura la responsabilité, lors de sa création, de générer ses « enfants » directs, puis chaque « enfant » aura à faire la même chose selon la structure. Par exemple, selon le MDD pour le jeu Risk à la figure 4.2,
JeuRisk
pourrait être l’objet racine, qui devra créer l’objetPlateauRisk
et les cinq instances deDé
. L’objetPlateauRisk
, lors de son initialisation, pourra instancier les 42 objetsPays
et les six objetsContinent
, en passant à chaqueContinent
ses objetsPays
lors de son initialisation. SiPlateauRisk
fournit une méthodegetPays(nom)
qui dépend d’un tableau associatif selon Transformer les identifiants en objets, alors c’est dans l’initialisation de cette classe que l’instance deMap<String,Pays>
sera créée. - Selon l’application, les objets peuvent être chargés en mémoire à partir d’un système de persistance, par exemple une base de données ou un fichier. Pour l’exemple de Risk,
PlateauRisk
pourrait charger, à partir d’un fichier JSON, des données pour initialiser toutes les instances dePays
. Pour une application d’inscription de cours à l’université, il se peut que toutes les descriptions de cours soient chargées en mémoire à partir d’une base de données. Une base de données amène un lot d’avantages et d’inconvénients, et elle n’est pas toujours nécessaire. Dans la méthodologie de ce manuel, on n’aborde pas le problème des bases de données (c’est le sujet d’un autre cours).
9.7 Réduire le décalage des représentations
Le principe du Décalage des représentations est la différence entre la modélisation (la représentation) du problème (du domaine) et la modélisation de la solution. Lorsqu’on fait l’ébauche d’une RDCU, on peut réduire le décalage des représentations principalement en s’inspirant des classes conceptuelles (du modèle du domaine) pour proposer des classes logicielles dans la solution décrite dans la RDCU. Plus une solution ressemble à la description du problème, plus elle sera facile à comprendre.
9.8 Pattern « Faire soi-même »
Dans la section F30.8/A33.7 , Larman mentionne le pattern « Faire soi-même » de Peter Coad (1997), qui permet de réduire le Décalage des représentations, même s’il ne représente pas exactement la réalité des objets (voir la figure 9.7 (a)) :
9.9 Exercices
Exercice 9.1 (Coder des méthodes à partir des diagrammes de séquence) Pour chacun des diagrammes suivants, écrivez les classes TypeScript avec les méthodes indiquées dans le diagramme
(cet exercice complémente le livre de Larman, 2005 à la section F18.6/A20.4 ) .
Voici un modèle à suivre. Pour le diagramme sur la figure 9.8, on code les classes suivantes en TypeScript :
class A {
: B; // A envoie un message à B, visibilité d'attribut
bexecute(arg0:number):any {
const result = this.b.setItem("Fred");
}
}
class B {
setItem(arg0:string):any {
//...
} }
Écrivez le code pour la figure suivante.
Écrivez le code pour la création de la collection de Vente (Larman, 2005), figure F17.6/A18.6 ].
Écrivez le code pour l’utilisation d’un Cornet (à dés, gobelet dans lequel on agite les dés) dans le jeu Monopoly (Larman, 2005), figure F22.9/A25.9 ].
Écrivez le code pour les appels polymorphes de la méthode
atterrirSur
dans le jeu Monopoly (Larman, 2005), figures F22.6/A25.6 et F22.7/A25.7 ].
Exercice 9.2 (RDCU pour le cas d’utilisation Ouvrir la caisse) Faites les RDCU pour le cas d’utilisation Ouvrir la caisse. Vous y trouverez également des artefacts tels que le DSS, les contrats d’opération et le modèle du domaine. Ils sont essentiels pour faire les RDCU selon la méthodologie présentée dans ce manuel.
Exercice 9.3 (Critique d’une conception) Dans cet exercice, l’objectif est de vous sensibiliser à la facilité de comprendre une conception à partir d’un problème. Un objectif secondaire est de considérer les choix de conception sur le plan de la cohésion et du couplage. Ici, il s’agit du jeu Monopoly, qui est un exemple proposé par Larman (2005), pour lequel il a également proposé un modèle du domaine et une conception, selon la méthodologie.
Pour cet exercice, nous examinerons une conception orientée objet réelle du jeu Monopoly disponible sur GitHub, soit Emojiopoly. Voici le travail à faire.
- Considérez les deux artefacts :
- un modèle du domaine de Monopoly proposé par Larman (2005) (il y a une version en français et une autre en anglais, puisque le code TypeScript est en anglais) ;
- un modèle d’une solution sous forme de diagramme de classes logicielles, créé à partir du code TypeScript dans le dépôt mentionné ci-dessus).
- Nous faisons une hypothèse que l’équipe qui a développé Emojiopoly n’a pas commencé avec le MDD de Larman.
- Comparez ces deux artefacts et faites des remarques sur la conception, surtout par rapport au MDD et au décalage des représentations.
- Faites des remarques sur la solution concernant la cohésion et le couplage.
Diagramme de classes d’Emojiopoly
Pour visualiser la conception, nous avons généré un diagramme de classes en UML sur la figure 9.9 avec l’outil tplant.
Modèle du domaine de Monopoly
Puisque la solution d’Emojiopoly est en anglais, vous pouvez regarder le modèle du domaine de Monopoly en français (figure F26.35 ) et en anglais (figure A31.35 ) pour vous aider à comprendre les termes.