-
Notifications
You must be signed in to change notification settings - Fork 2
/
RDCU.qmd
557 lines (471 loc) · 28.8 KB
/
RDCU.qmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
# Réalisations de cas d'utilisation (RDCU) {#sec-rdcu}
Les réalisations de cas d'utilisation (RDCU) sont le sujet du chapitre F17/A18\ {{< fa solid book >}}.
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 @fig-RDCU_ID_en_objets.
- Le livre de @craig_uml_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 @fig-RDCU_Aide_Memoire.
```{.plantuml genurl="true" #fig-RDCU_Aide_Memoire caption="Aide-mémoire pour faire une RDCU. L'étape en rouge nécessite beaucoup de pratique, selon la complexité des postconditions. Vous pouvez vous attendre à ne pas la réussir du premier coup." }
@startuml
!include normal.pumlinclude
' get font awesome icon
!define ICONURL https://raw.githubusercontent.com/tupadr3/plantuml-icon-font-sprites/v2.4.0
!includeurl ICONURL/common.puml
!include ICONURL/font-awesome-5/book.puml
'
skinparam shadowing false
skinparam DefaultTextAlignment center
skinparam NoteTextAlignment left
start
repeat
:Revoir l'opération système;
note right: ex. : ""**créerNouvelleVente()**""
#ddffdd:Déterminer le contrôleur;
note right: Appliquer **GRASP Contrôleur**\n(chapitre F16.13/A17.13 <$book{scale=0.3}>)
:Rappeler le contrat d'opération;
note right: Les **postconditions**, p. ex. :\n - Une instance <i>v</i> de Vente a été créée\n - <i>v</i> a été associée au Registre\n - Des attributs de <i>v</i> ont été initialisés
repeat
#ffdddd:Concevoir (et raffiner) un diagramme d'interaction
pour l'opération système, **satisfaisant toutes**
**les postconditions** du contrat d'opération et
**retournant l'information selon le message de**
**retour** du DSS, le cas échéant;
note right
Appliquer **GRASP Créateur**,
**GRASP Expert**, **ID en objets**,
etc. (chapitre F16.8/A17.8 <$book{scale=0.3}>).
end note
repeat while (reste des postconditions insatisfaites\nou des infos pas encore retournées)
repeat while (reste des opérations système)
stop
legend top
Note : Une solution détaillée est faite pour chaque opération système.
Donc, il faut utiliser le DSS, les contrats d'opération, le MDD et les
principes GRASP pour ce travail.
endlegend
@enduml
```
## 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 @tbl-tableGRASP.
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](#sec-rdcu_Demarrer).
## Satisfaire les postconditions
### 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 @tbl-tableGRASP.
::: {.callout-caution}
Une erreur potentielle est de donner la responsabilité de créer à un contrôleur, puisqu'*il a les données pour initialiser* l'objet. Bien que ce soit justifiable par le principe GRASP Créateur, il vaut mieux favoriser une classe qui *agrège* l'objet à créer, si nécessaire.
:::
### 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'objet `a` (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 @sec-contrat_exemple), alors il faut repérer le bon objet d'abord. Voir la section [Transformer identifiants en objets](#sec-TransformerIDenObjets).
Dans la plupart des cas, la justification pour former une association est GRASP Expert, défini dans le @tbl-tableGRASP.
Il faut faire attention à la [visibilité](#sec-Visibilite)\ {{< fa solid low-vision >}}.
### 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 @tbl-tableGRASP.
Très souvent, c'est une méthode `setX(valeur)`, où `X` correspond à l'attribut qui sera modifié à `valeur`.
Attention à la [visibilité](#sec-Visibilite)\ {{< fa solid low-vision >}}.
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 @fig-RDCU_Constructeur_Modification.
```{.plantuml genurl="true" #fig-RDCU_Constructeur_Modification caption="Combiner la création d'une instance et une modification de son attribut dans un constructeur." }
@startuml
!include ecriture.pumlinclude
scale 1.3
skinparam sequenceMessageAlign left
participant ":Plateau" as p
participant ":Case" as c
loop
create c
p -->> c : create(nom)
end loop
@enduml
```
## Visibilité {#sec-Visibilite}
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*.
::: {.callout-warning}
Dans un diagramme de séquence pour une RDCU, il est facile de montrer des objets *a* et *b* et de dessiner une flèche entre l'objet *a* et *b* puis d'écrire le nom d'une méthode pour représenter l'appel.
Cependant, dans un programme codé en TypeScript ou en Java, il y aura des erreurs de compilation si *a* n'a pas une référence pour *b*.
L'approche de Larman propose d'être sensible à la visibilité lors de la conception (en faisant les RDCU), car ajouter une visibilité implique l'augmentation du couplage.
:::
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](https://stackoverflow.com/a/2085988/1168342)\ {{< fa brands 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 @fig-RDCU_Visibilite1, 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)\ {{< fa solid book >}}.
```{.plantuml genurl="true" #fig-RDCU_Visibilite1 caption="L'objet *b* doit être visible à l'objet *a* si *a* veut lui envoyer un message." }
@startuml
!include ecriture.pumlinclude
scale 1.3
skinparam sequenceMessageAlign left
participant "a:A" as A
participant "b:B" as B
-> A : ...
A -> B : unMessage()
B -> : ...
@enduml
```
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](#sec-rdcu_Demarrer).
## Transformer les identifiants en objets {#sec-TransformerIDenObjets}
La directive d'utiliser les types primitifs pour les arguments dans les opérations système (voir la @sec-dss) 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**\ {{< fa solid book >}}), qui sert à repérer l'objet qui correspond à l'identifiant.
Il y a un exemple à la @fig-RDCU_ID_en_objets provenant du chapitre sur **l'Application des patterns GoF (figure F23.18\ {{< fa solid book >}})**.
Un autre exemple du livre de @craig_uml_2005 est l'identifiant `codeArticle` transformé en objet `DescriptionProduit` par la méthode
`CatalogueProduits.getDescProduit(codeArticle:String):DescriptionProduit`.
```{.plantuml genurl="true" #fig-RDCU_ID_en_objets caption="Un identifiant `idClient:String` est transformé en objet `c:Client`, qui est ensuite envoyé à la Vente en cours." }
@startuml
!include ecriture.pumlinclude
scale 1.1
skinparam sequenceMessageAlign center
participant ":Registre" as R
participant ":Magasin" as M
participant "v:Vente" as v
-> R : saisirClientPourRemise(idClient)
R -> M : c = getClient(idClient)
note right: Selon Expert et\n**les ID en objets**
R -> v : saisirClientPourRemise(c:Client)
@enduml
```
La @sec-UtilisationMap explique comment implémenter la transformation avec un tableau associatif.
## Utilisation d'un tableau associatif (`Map<clé, objet>`) {#sec-UtilisationMap}
Pour *transformer un identifiant en objet*, il est pratique d'utiliser un [tableau associatif (aussi appelé dictionnaire ou *map* en anglais)](https://fr.wikipedia.org/wiki/Tableau_associatif)\ {{< fa brands wikipedia-w >}}.
L'exemple du livre de @craig_uml_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\ {{< fa solid book >}}.
Notez que les exemples de @craig_uml_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 @fig-Map_Case_Monopoly en est un exemple.
```{.plantuml genurl="true" #fig-Map_Case_Monopoly caption="Exemple de l'utilisation d'un tableau associatif pour trouver une Case Monopoly à partir de son nom." }
@startuml
!include ecriture.pumlinclude
scale 1.1
skinparam sequenceMessageAlign center
participant ":Plateau" as P
participant "cMap\n:Map<String, Case>" as m
-> P : s = getCase(nom)
note right : Plateau agrège toutes les\nCases et possède un Map\navec nom comme clé.
'P -> P : cMap = getToutesCases
P -> m : c = get(nom) : Case
@enduml
```
Dans la section suivante, l'initialisation des éléments utilisés dans les RDCU (comme des tableaux associatifs) est expliquée.
## RDCU pour l'initialisation, le scénario Démarrer {#sec-rdcu_Demarrer}
Le lancement de l'application correspond à la RDCU "Démarrer".
La section **Initialisation et cas d'utilisation Démarrer** (F17.4\ {{< fa solid book >}}, p. 345) ou **Initialization and the *Start Up* Use Case** (A18.4\ {{< fa solid book >}}, 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 @fig-MDD-jeu-de-risk, `JeuRisk` pourrait être l'objet racine, qui devra créer l'objet `PlateauRisk` et les cinq instances de `Dé`. L'objet `PlateauRisk`, lors de son initialisation, pourra instancier les 42 objets `Pays` et les six objets `Continent`, en passant à chaque `Continent` ses objets `Pays` lors de son initialisation. Si `PlateauRisk` fournit une méthode `getPays(nom)` qui dépend d'un tableau associatif selon [Transformer les identifiants en objets](#sec-TransformerIDenObjets), alors c'est dans l'initialisation de cette classe que l'instance de `Map<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 de `Pays`. 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).
```{.plantuml genurl="true" #fig-DemarrerRisk caption="Exemple de l'initialisation partielle du jeu Risk, avec les principes GRASP et les tableaux associatifs pour faciliter la transformation d'identifiants en objets." }
@startuml
!include ecriture.pumlinclude
scale 0.75
skinparam sequenceMessageAlign center
participant ":ObjetMain" as outside
participant ":JeuRisk" as jr
participant ":Dé" as d
participant ":PlateauRisk" as pr
participant ":Continent" as c
participant "p:Pays" as p
participant "pMap\n:Map<String,\nPays>" as m
create jr
outside -->> jr : create
note right: JeuRisk est l'objet racine. **Contrôleur** ne s'applique pas ici, car il ne s'agit pas d'une opération système
loop i<5
create d
jr -->> d : dés[i] = create
note over jr,d: par **Créateur**\nJeuRisk agrège Dé
end loop
create pr
jr -->> pr : create
note over jr,pr: par **Créateur**\nJeuRisk agrège PlateauRisk
create m
pr -->> m : pMap = create
note over pr,m: par **Créateur**: PlateauRisk agrège Map<String, Pays>
pr -> pr : continentsAvecPays[] =\nchargerContinentsAvecPays
note right : Par **Expert**\nCharger les données d'un fichier JSON
' continents
loop i<continentsAvecPays.size
create c
pr -->> c : create(continentsAvecPays[i].nom, ...)
note over pr,c: par **Créateur**: PlateauRisk agrège Continent
loop j<continentsAvecPays[i].pays.size
create p
pr -->> p : p = create(continentsAvecPays[i].pays[j].nom, ...)
note over pr,p: par **Créateur**: PlateauRisk agrège Pays
pr -> m : add(p)
note right: Par **Expert**
pr -> c : add(p)
note right: Par **Expert**
end loop
end loop
@enduml
```
## Réduire le décalage des représentations
Le principe du [Décalage des représentations](#sec-DecalageRepresentations) 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.
::: {.callout-caution}
Une application de patterns GoF à la solution peut nuire à ce principe, car ces patterns ajoutent souvent des classes logicielles n'ayant aucun lien avec le modèle du domaine.
Par exemple, un Visiteur ou un Itérateur sont des classes logicielles sans binôme dans le modèle du domaine.
Il faut vérifier avec une personne expérimentée (l'architecte du projet si possible) que l'application du pattern est justifiée, qu'elle apporte de vrais bénéfices au design en dépit des désavantages dus à des classes ajoutées.
Chaque fois qu'on propose des classes logicielles qui n'ont pas de liens avec la représentation du problème du domaine, on *augmente le décalage des représentations* et l'on rend la solution un peu plus difficile à comprendre.
C'est aussi une forme de [Complexité circonstancielle (provenant des choix de conception)](#sec-ComplexiteCirc).
Ce dilemme est un bon exemple de la nature pernicieuse de la conception de logiciels.
Il est très difficile, même pour les spécialistes en conception, de trouver un bon équilibre entre toutes les forces: la maintenabilité, la simplicité, les fonctionnalités, etc.
Vous pouvez en lire plus dans [cette réponse sur Stack Overflow](https://stackoverflow.com/a/18702842/1168342)\ {{< fa brands stack-overflow >}}.
:::
## Pattern "Faire soi-même" {#sec-FaireSoiMeme}
Dans la section F30.8/A33.7\ {{< fa solid book >}}, Larman mentionne le pattern "Faire soi-même" de Peter Coad [-@coad97a], qui permet de réduire le [Décalage des représentations](#sec-DecalageRepresentations), même s'il ne représente pas exactement la réalité des objets (voir la @fig-main_des):
::: {#fig-RDCU_faire_soi_meme layout-ncol=2 layout-valign="bottom"}
![Dés dans la vraie vie (["Hand of chance"](https://www.flickr.com/photos/aerust/9590772048/) [(CC BY 2.0)](https://creativecommons.org/licenses/by/2.0/) par [Alexandra E Rust](https://www.flickr.com/people/aerust/)).](images/flickr_aerust_hand_of_chance.jpg){width=80% #fig-main_des}
```{#fig-de_logiciel .plantuml genurl="true" caption="Dé dans un logiciel selon *Faire soi-même*." }
!include ecriture.pumlinclude
hide empty methods
class Dé {
+face : int
brasser()
}
```
**Faire soi-même**: "Moi, objet logiciel, je fais moi-même ce qu'on fait normalement à l'objet réel dont je suis une abstraction" de @coad97a.
:::
## Exercices
::: {#exr-RDCU_coder}
### 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 @craig_uml_2005 à la section F18.6/A20.4\ {{< fa solid book >}}] .
::: {.callout-tip}
Vous pouvez utiliser VS Code pour vous aider avec le TypeScript, mais cet outil ne sera pas forcément permis lors d'un examen.
:::
```{.plantuml genurl="true" #fig-coder_sequence_modele caption="Exemple de diagramme de séquence." }
@startuml
scale 1.0
!include normal.pumlinclude
participant ":A" as B
participant ":B" as A
[-> B : execute(3)
activate B
B -> A : result = setItem("Fred")
activate A
deactivate A
deactivate B
@enduml
```
Voici un modèle à suivre.
Pour le diagramme sur la @fig-coder_sequence_modele, on code les classes suivantes en TypeScript:
```typescript
class A {
b: B; // A envoie un message à B, visibilité d'attribut
execute(arg0:number):any {
const result = this.b.setItem("Fred");
}
}
class B {
setItem(arg0:string):any {
//...
}
}
```
\filbreak
1. Écrivez le code pour la figure suivante.
```{.plantuml}
@startuml
!include normal.pumlinclude
scale 1.0
participant ":Bernard" as Bob
participant ":Alice" as Alice
participant ":Autre" as Other
[-> Bob : init
activate Bob
Bob -> Alice : allô(12)
activate Alice
create Other
Alice --> Other : create
Alice -->> Bob : "oui"
deactivate Alice
[<<-- Bob : 15
deactivate Bob
@enduml
```
2. Écrivez le code pour la création de la collection de Vente [@craig_uml_2005], figure F17.6/A18.6\ {{< fa solid book >}}].
3. Écrivez le code pour l'utilisation d'un *Cornet* (à dés, gobelet dans lequel on agite les dés) dans le jeu Monopoly [@craig_uml_2005], figure F22.9/A25.9\ {{< fa solid book >}}].
4. Écrivez le code pour les appels polymorphes de la méthode `atterrirSur` dans le jeu Monopoly [@craig_uml_2005], figures F22.6/A25.6 et F22.7/A25.7\ {{< fa solid book >}}].
:::
::: {#exr-RDCU_Ouvrir_caisse}
### RDCU pour le cas d'utilisation *Ouvrir la caisse*
Faites les RDCU pour [le cas d'utilisation *Ouvrir la caisse*](#sec-CU_Ouvrir_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.
:::
::: {#exr-RDCU_Critiquer_conception}
### 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 @craig_uml_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](https://github.com/Chuzzy/Emojiopoly). Voici le travail à faire.
- Considérez les deux artefacts:
- un [modèle du domaine de Monopoly](#sec-MDD_Monopoly) proposé par @craig_uml_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](#sec-DDC_Emojiopoly) 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 {#sec-DDC_Emojiopoly .unnumbered .unlisted}
Pour visualiser la conception, nous avons généré un diagramme de classes en UML sur la @fig-Emojiopoly_classes avec l'outil [tplant](https://github.com/bafolts/tplant).
```{.plantuml genurl="true" caption="Diagramme de classes logicielles (TypeScript) pour le projet [Emojiopoly](https://github.com/Chuzzy/Emojiopoly)." #fig-Emojiopoly_classes}
@startuml
' généré avec tplant a partir d'un clone de https://github.com/Chuzzy/Emojiopoly :
' tplant --input src/monopoly/**/*.ts --output Emojiopoly.puml -A
' (certaines éléments ont été modifiés pour la mise en page)
'left to right direction
scale 0.6
interface Card {
+text: string
+action: "advance" | "back" | "choice" | "payeach" | "collecteach" |\n "doubletransport" | "earn" | "pay" | "stealmoney" | "stolenmoney" |\n "streetrepairs" | "gotojail"
+value?: string | number | number[]
}
class MonopolyGame {
+jackpot: number
+currentTurnIndex: number
+consecutiveDoubles: number
+players: Player[]
+currentPlayer: Player
+dice: number[]
+isTurnFinished: boolean
+unpaidDebts: Debt[]
+messageEventHandler: (message: string) => void
+chanceCards: Card[]
+chestCards: Card[]
+chanceCardIndex: number
+chestCardIndex: number
+addToJackpot(amount: number): void
+rollDice(die1?: number, die2?: number): void
+moveToSquare(newSquare: Square, awardSalary?: boolean): void
+sendToJail(): void
+moveBack(numberOfSpaces: number): void
+postBail(): void
+takeChance(): void
+takeChest(): void
+handleCard(card: Card): void
+movePlayer(): void
+payDebts(): void
+finishTurn(): void
+board: Square[]
+playerNames: string[]
+houseRules: HouseRules
}
interface HouseRules {
' +needMonopolyToBuild: boolean
' +doubleRentWithMortgages: boolean
' +evenBuildRule: boolean
' +rentInJail: boolean
' +extraTurnOnRollingOutOfJail: boolean
' +tradeOutOfBankruptcy: boolean
' +bankCoversUnpaidRent: boolean
' +bankruptAssetsAlwaysToBank: boolean
' +purchaseMortgagedProperties: boolean
' +freeParkingJackpot: number | "fines"
' +goSalary: number
' +landOnGo: "normal" | "double" | "freemove"
' +startingMoney: number
' +turnsInJail: number
' +bailAmount: number
' +maxConsecutiveDoubles: number
' +houseLimit: number
' +hotelLimit: number
' +mortgageRate: number
' +unmortgageRate: number
' +maintenanceRate: number
' +incomeTax: number
' +superTax: number
}
class Player {
+ownedProperties: Property[]
+currentSquare: Square
+turnsInJail: number
+name: string
+money: number
}
class Property {
+buildingsCount: number
+isMortgaged: boolean
+owner: Player
+name: string
+color: "brown" | "lightblue" | "pink" | "orange" | "red" | "yellow" |\n "green" | "blue" | "white"
+price: number
+rent: number[] | "transport" | "utility"
+buildPrice: number
}
class Square {
+occupants: Player[]
+addOccupant(occupant: Player): void
+removeOccupant(occupant: Player): void
+squareContents: "gotojail" | Property | "go" | "jail" | "parking" |\n "chance" | "chest" | "incometax" | "supertax"
}
class Debt {
+amount: number
+payDebt(): void
+toString(): string
+game: MonopolyGame
+debtor: Player
+creditor: Player
+initialAmount: number
}
MonopolyGame --> "*" Player
' MonopolyGame --> "1" Player
MonopolyGame --> "*" Debt
MonopolyGame --> "*" Card
' MonopolyGame --> "1" Square
' MonopolyGame --> "1" Card
MonopolyGame --> "*" Square
MonopolyGame --> "1" HouseRules
Player --> "*" Property
Player --> "1" Square
Property --> "1" Player
Square --> "*" Player
' Square --> "1" Player
Square --> "1" Property
Debt --> "1" MonopolyGame
Debt --> "1" Player
@enduml
```
### Modèle du domaine de Monopoly {#sec-MDD_Monopoly .unnumbered .unlisted}
Puisque la solution d'Emojiopoly est en anglais, vous pouvez regarder le modèle du domaine de Monopoly en français (figure F26.35 {{< fa solid book >}}) et en anglais (figure A31.35 {{< fa solid book >}}) pour vous aider à comprendre les termes.
:::