Tâche: Conception de cas d'utilisation
Cette tâche définit la manière de détailler les produits de l'analyse de cas d'utilisation en développant des réalisation de cas d'utilisation de niveau conception.
Disciplines: Analyse et conception
Objet
  • Détailler les réalisations de cas d'utilisation en termes d'interactions
  • Détailler les exigences concernant les opérations des classes de conception
  • Détailler les exigences concernant les opérations des sous-systèmes de conception et/ou de leurs interfaces
  • Détailler les exigences concernant les opérations des capsules
Relations
Description principale

Il est possible de décrire le comportement d'un système à l'aide d'un certain nombre de techniques - collaborations ou interactions. Cette tâche décrit l'utilisation des interactions, en particulier les diagrammes de séquence, pour décrire le comportement du système. Les diagrammes de séquence sont le plus utiles lorsque le comportement du système ou du sous-système peut d'emblée être décrit par messagerie synchrone. La messagerie asynchrone, en particulier dans les systèmes commandés par les événements, est souvent plus facilement décrite en termes de machines d'état et de collaborations, permettant de définir de manière compacte les interactions possibles entre les objets. Les messages asynchrones jouent un rôle important dans les systèmes réactifs et à temps réel et sont utilisés pour une communication entre les instances du  Produit : Capsule.

 Représentation UML 1.x

Vous pouvez utiliser une classe de proxy pour représenter le sous-système sur des diagrammes de séquence. Cette classe de proxy se trouve au sein du sous-système et est utilisée pour représenter le sous-système sous forme de diagrammes qui ne prennent pas en charge l'utilisation directe des packages et des sous-systèmes en tant qu'éléments comportementaux. Utilisez la classe de proxy dans les cas où vous voulez montrer qu'un sous-système spécifique répond à un message. Dans ce cas, vous pouvez afficher des messages envoyés à partir du proxy du sous-système vers d'autres objets.

Référez-vous à Différences entre UML 1.x et UML 2.0 pour plus d'informations.

Etapes
Créer des réalisations de cas d'utilisation

Le Produit : Réalisation de cas d'utilisation de conception offre la possibilité de tracer le comportement du modèle de conception dans le modèle de cas d'utilisation et d'organiser des collaborations dans le modèle de conception autour du concept de cas d'utilisation.

Créer une réalisation de cas d'utilisation de conception dans le modèle de conception pour chacun des cas d'utilisation à concevoir. Le nom de la réalisation de cas d'utilisation de conception doit être le même que celui du cas d'utilisation associé et une relation de "réalisation" doit être établie à partir de la réalisation de cas d'utilisation vers son cas d'utilisation associé.

Décrire les interactions entre les objets de conception

Pour chaque réalisation de cas d'utilisation, vous devez illustrer les interactions entre les objets de conception qui y participent en créant un ou plusieurs diagrammes de séquence. Des versions antérieures peuvent avoir été créées lors de la Tâche : Analyse de cas d'utilisation . De telles "versions d'analyse" des réalisations de cas d'utilisation décrivent les interactions entre les classes d'analyse. Elles doivent être modifiées pour décrire les interactions entre les éléments de conception.

La mise à jour des diagrammes de séquence implique les étapes suivantes :

  • Identifier chaque objet participant au flux du cas d'utilisation. Cela est possible en instanciant les classes et sous-systèmes de conception identifiés dans la Tâche : Identifier les éléments de conception. Dans les systèmes en temps réel, vous identifierez également les instances de capsule qui participent au flux du cas d'utilisation.
  • Représenter chaque objet participant à un diagramme de séquence. Etablir une ligne de vie pour chaque objet participant au diagramme de séquence. Vous disposez de plusieurs choix pour représenter les sous-systèmes de conception :
    • Vous pouvez afficher les instances du sous-système sur le diagramme de séquence.
    • Vous pouvez utiliser les interfaces réalisées par le sous-système. Cela est préférable dans le cas où vous voulez montrer que tout élément du modèle réalisant la même interface peut être utilisé à la place de l'interface. Si vous choisissez d'afficher les interfaces sur le diagramme de séquence, soyez certain que vous voulez vous assurer qu'aucun message ne sera envoyé à partir de l'interface vers d'autres objets. Cela est dû au fait que les interfaces encapsulent complètement la réalisation interne des opérations. Toutefois, il n'est pas certain que tous les éléments du modèle qui réalisent l'interface seront en réalité conçus de la même manière. En conséquence, sur les diagrammes de séquence, aucun message ne doit être affiché comme étant envoyé à partir d'interfaces.
    • Vous pouvez utiliser le composant pour représenter le sous-système sur les diagrammes de séquence. Utilisez le composant dans le cas où vous voulez montrer qu'un sous-système spécifique répond à un message. Dans ce cas, vous pouvez afficher des messages comme étant envoyés à partir du composant vers d'autres objets.

    Notez qu'il existe des diagrammes de séquence de niveau système qui affichent la manière dont les instances des éléments de conception de haut niveau (généralement les sous-systèmes et les interfaces des sous-systèmes) entrent en interaction. Les diagrammes de séquence affichant la conception interne des sous-systèmes sont produits séparément, dans le cadre de la Tâche : Conception de sous-système.

  • Noter que les interactions d'objet actif sont généralement décrites à l'aide de collaborations de spécification et de machines d'état. Elles sont utilisées pour montrer la manière dont les messages peuvent être envoyés vers des objets actifs par d'autres éléments dans le système dans une réalisation de cas d'utilisation plus importante. De manière générale, les objets actifs sont encapsulés au sein des sous-systèmes dans le cadre de cette tâche, de telle manière que la réalisation de cas d'utilisation consiste en un ensemble de sous-systèmes en interaction. Les interactions définissent les responsabilités et les interfaces des sous-systèmes. Au sein des sous-systèmes, les objets actifs représentent des unités d'exécution concurrentes. Les sous-systèmes permettent de diviser le travail entre les équipes de développement, avec les interfaces servant de contrats formels entre les équipes. Pour les systèmes en temps réel, vous utiliserez le  Produit : Capsule pour représenter les objets actifs.

    Remarque mineure concernant l'affichage des messages émanant des sous-systèmes : restreindre les messages aux seules interfaces réduit le couplage entre les éléments du modèle et améliore la résilience de la conception. Lorsque cela est possible, vous devez essayer d'y arriver et lorsqu'il existe des messages émanant de sous-systèmes vers des éléments de modèle non constitutifs de l'interface, vous devez considérer la possibilité de les changer en messages vers des interfaces afin d'améliorer le découplage dans le modèle.

  • Représenter l'interaction qui existe entre les acteurs. Représentez par une ligne de vie dans le diagramme de séquence chaque instance d'acteur et objet externe interagissant avec les objets participants.
  • Illustrer l'envoi de messages entre les objets participants. Le flux d'événements commence en haut du diagramme et se poursuit vers le bas, en indiquant un axe chronologique vertical. Illustrez l'envoi de messages entre les objets en créant des messages (flèches) entre les lignes de vie. Le nom du message doit être le nom de l'opération appelée par le message. Lors des premières étapes de la conception, peu d'opérations seront affectées aux objets, il est donc possible que vous ignoriez ces informations et que vous donniez au message un nom temporaire ; de tels messages sont considérés comme étant "non affectés." Ultérieurement, lorsque vous aurez trouvé plus d'opérations avec des objets participants, vous devrez mettre à jour le diagramme de séquence en "affectant" les messages aux opérations.
  • Décrire ce que l'objet fait lorsqu'il reçoit un message. Pour ce faire, il est nécessaire d'associer un script au message correspondant. Placez ces scripts dans la marge du diagramme. Utilisez du texte structuré ou un pseudocode. Si vous utilisez un pseudocode, assurez-vous d'utiliser des blocs de construction dans le langage d'implémentation afin de faciliter l'implémentation des opérations correspondantes. Lorsque la personne responsable d'une classe d'objet affecte et définit ses opérations, les scripts de l'objet fourniront une base de travail.

Diagramme décrit dans le texte d'accompagnement.

Vous documentez le comportement de cas d'utilisation adopté par les objets dans un diagramme de séquence.

Lorsque vous avez un comportement réparti parmi les objets, vous devez considérer la manière dont le flux sera contrôlé. Vous avez trouvé les objets en supposant qu'ils entreront en interaction d'une certaine manière dans la réalisation de cas d'utilisation et auront un certain rôle. Lorsque vous distribuez le comportement, vous pouvez commencer par tester ces suppositions. Dans certaines parties du flux, il est possible que vous souhaitiez utiliser une structure décentralisée ; dans d'autres parties, vous pourriez préférer une structure centralisée. Pour la définition de ces variantes et recommandations sur le moment où utiliser les deux types de structure, voir Technique : Diagrammes de séquence.

Il est possible que vous ayez besoin de nouveaux objets à ce stade, par exemple si vous utilisez une structure centralisée et que vous avez besoin d'un nouvel objet pour contrôler le flux. Souvenez-vous que chaque objet que vous ajoutez au modèle de conception doit remplir les exigences relatives au modèle d'objet.

Intégrer des mécanismes de conception applicables

Lors de la Tâche : Analyse architecturale, les mécanismes d'analyse ont été identifiés. Lors de la Tâche : Identifier les mécanismes de conception, les mécanismes d'analyse sont détaillés dans des mécanismes de conception, le mappage des mécanismes d'analyse vers les mécanismes de conception est capturé dans le document d'architecture logicielle et les mécanismes de conception sont documentés dans les  instructions spécifiques au projet.  

Lors de cette tâche, la conception de cas d'utilisation et tout mécanisme de conception applicable sont intégrés dans les réalisations de cas d'utilisation. L e concepteur étudie les mécanismes de conception applicables et détermine ceux qui s'appliquent à la réalisation de cas d'utilisation développée, en travaillant dans le cadre des recommandations et des instructions documentées dans le document d'architecture logicielle et dans les principes de conception.  
Remarque : le mécanisme de conception applicable peut avoir été identifié dans la Tâche : Analyse de cas d'utilisation, pendant laquelle les classes d'analyse peuvent avoir été "étiquetées" avec un mécanisme d'analyse particulier, indiquant qu'un élément particulier de fonctionnalité a dû être traité dans la conception. Dans un tel cas, les mécanismes de conception applicables sont ceux qui sont associés aux mécanismes d'analyse avec lesquels les classes d'analyse participant à la réalisation de cas d'utilisation ont été étiquetées.

Le concepteur intègre les mécanismes de conception applicables dans les réalisations de cas d'utilisation en incluant les éléments de conception nécessaires et les interactions d'élément de conception dans les réalisations de cas d'utilisation en suivant les règles d'utilisation documentées dans les principes de conception.

Traiter toutes les variantes du flux d'événements

Vous devez décrire chaque variante de flux dans un diagramme de séquence séparé. Les diagrammes de séquence sont généralement préférables aux diagrammes de communication dans la mesure où ils sont plus faciles à lire lorsque le diagramme contient le niveau de détails généralement requis pour la conception de système.

Commencez par décrire le flux de base, qui est le flux d'événements le plus courant ou le plus important. Décrivez ensuite les variantes comme les flux exceptionnels. Il n'est pas nécessaire de décrire tous les flux d'événements, à partir du moment où vous employez et illustrez toutes les opérations des objets participants. Dans ces conditions, seuls de petits flux peuvent être omis comme ceux concernant un seul objet.

Etudiez le cas d'utilisation pour voir s'il existe des variantes du flux, différentes de celles qui ont déjà été décrites dans l'analyse et la capture des exigences, par exemple, celles qui dépendent de l'implémentation. Lorsque vous identifiez de nouveaux flux, décrivez chaque flux dans un diagramme de séquence. Ci-dessous sont recensés quelques exemples de flux exceptionnels :

  • Traitement d'erreurs. Si une interface génère un rapport concernant une erreur dans sa communication avec un système externe, par exemple, le cas d'utilisation doit en tenir compte. Une solution possible consiste à ouvrir une nouvelle voie de communication.
  • Traitement du délai d'attente. Si l'utilisateur ne répond pas au cours d'une période définie, le cas d'utilisation doit prendre des mesures particulières.
  • Traitement d'entrée erronée vers les objets participant au cas d'utilisation. Ce type d'erreurs peut provenir d'une entrée utilisateur incorrecte.

Traiter les composants facultatifs du cas d'utilisation

Vous pouvez décrire un chemin alternatif d'un flux en tant que flux facultatif au lieu d'une variante. La liste suivante recense deux exemples de flux facultatifs.

  • En envoyant un signal, l'acteur décide - à partir d'un certain nombre d'options - ce que le cas d'utilisation va faire ensuite. Le cas d'utilisation a demandé à l'acteur de répondre oui ou non à une question, par exemple, ou a fourni à l'acteur une série de fonctions que le système peut exécuter dans l'état actuel du cas d'utilisation.
  • Le chemin du flux varie en fonction de la valeur des attributs stockés ou des relations. Le flux d'événements consécutif dépend du type de données à traiter.

Si vous voulez distinguer un flux facultatif ou un sous-flot complexe, utilisez un diagramme de séquence séparé. Chaque diagramme de séquence séparé doit être référencé à partir du diagramme de séquence du flux d'événements principal utilisant des scripts, du texte en marge ou des notes indiquant où le comportement facultatif ou de sous-flot a lieu.

Au cas où le comportement du flux exceptionnel ou facultatif peut se produire n'importe où, par exemple un comportement exécuté lorsque un événement particulier se produit, le diagramme de séquence du flux d'événements principal doit être annoté pour indiquer que lorsque l'événement se produit, le comportement décrit dans le diagramme de séquence facultatif/exceptionnel sera exécuté. De manière alternative, s'il existe un comportement déclenché par un événement, vous pouvez considérer l'utilisation de diagrammes état-transition pour décrire le comportement du système. Pour plus d'informations, voir Instructions : Diagramme état-transition.

Simplifier les diagrammes de séquence en utilisant des sous-systèmes (facultatif)

Lorsqu'un cas d'utilisation est réalisé, le flux d'événements est en général décrit en termes d'objets d'exécution, c'est-à-dire en tant qu'interaction entre les objets de conception. Pour simplifier les diagrammes et identifier le comportement réutilisable, il est possible qu'il soit nécessaire d'encapsuler un sous-flot d'événement au sein d'un sous-système. Dans ce cas, de larges sous-sections de diagramme de séquence sont remplacées par un message unique vers le sous-système. Au sein du sous-système, un diagramme de séquence séparé peut illustrer les interactions internes au sein du sous-système qui fournissent le comportement requis (pour plus d'informations, voir Tâche : Conception de sous-système).

Les sous-séquences de messages au sein des diagrammes de séquence doivent être encapsulées au sein du sous-système lorsque :

  • La sous-séquence se produit de manière répétée au cours de différentes réalisations de cas d'utilisation ; c'est-à-dire, les mêmes messages (ou similaires) sont envoyés aux mêmes objets (ou similaires), offrant le même résultat final. Le terme 'similaire' est utilisé parce qu'un certain travail de conception peut être nécessaire pour rendre le comportement réutilisable.
  • La sous-séquence se produit dans une seule réalisation de cas d'utilisation, mail il est probable qu'elle soit répétée dans des itérations futures ou dans des systèmes similaires à l'avenir. Il se peut que le comportement constitue un bon composant réutilisable.
  • La sous-séquence se produit dans une seule réalisation de cas d'utilisation, elle est complexe mais facilement encapsulée et doit être de la responsabilité d'une personne ou d'une équipe, et elle fournit un résultat bien défini. Dans ce type de situations, le comportement complexe nécessite en général une connaissance technique ou une connaissance spécifique du domaine et, en conséquence, se prête bien à être encapsulé au sein d'un sous-système.
  • La sous-séquence est destinée à être encapsulée au sein d'un composant remplaçable (voir Concept : Composant). Dans ce cas, un sous-système constitue la représentation appropriée du composant au sein du modèle de conception.

Diagramme décrit dans le texte d'accompagnement.

Une réalisation de cas d'utilisation peut être décrite, si nécessaire, à plusieurs niveaux dans la hiérarchie du sous-système. Les lignes de vie du diagramme intermédiaire représentent les sous-systèmes ; les interactions dans les cercles représentent l'interaction interne des membres du sous-système en réponse au message.

Les avantages de cette approche sont :

  • Les réalisations de cas d'utilisation sont plus claires, en particulier si la conception interne de certains sous-systèmes est complexe.
  • Il est possible de créer des réalisations de cas d'utilisation avant de créer les conceptions internes des sous-systèmes ; cela est très utile par exemple dans des environnements de développement parallèles (voir "Comment travailler en parallèle").
  • Les réalisations de cas d'utilisation sont plus génériques et plus simples à changer, en particulier si un sous-système doit être remplacé par un autre sous-système.

Exemple :

Considérez le diagramme de séquence suivant, partie intégrante d'une réalisation de cas d'utilisation Appel local :

Diagramme décrit dans le texte d'accompagnement.

Dans ce diagramme, les classes grises appartiennent au sous-système de gestion du réseau ; les autres classes appartiennent au sous-système de gestion des abonnés. Cela signifie qu'il s'agit d'un diagramme de séquence à sous-systèmes multiples, c'est-à-dire un diagramme où tous les objets participant au flux d'événements sont inclus, peu importe si leurs classes dépendent de différents sous-systèmes ou non.

De manière alternative, il est possible d'afficher l'appel du comportement d'un sous-système de gestion du réseau et la pratique d'une interface particulière sur ce sous-système. En partant du principe que le sous-système de gestion du réseau fournit une interface ICoordinator utilisée par le sous-système de gestion des abonnés :

Diagramme décrit dans le texte d'accompagnement.

L'interface ICoordinator est réalisée par la classe Coordinateur au sein de la gestion du réseau. De ce fait, il est possible d'utiliser le sous-système de gestion du réseau lui-même et son interface ICoordinator dans le diagramme de séquence, plutôt que d'utiliser des instances de classes au sein de la gestion du réseau :

Diagramme décrit dans le texte d'accompagnement.

Notez que le coordinateur, les informations numériques et les instances de classe de réseau sont remplacés par le sous-système qui les contient. Tous les appels vers le sous-système sont effectués via l'interface ICoordinator.

Affichage des interfaces sur les lignes de vie

Afin d'arriver à une véritable substituabilité des sous-systèmes réalisant la même interface, seule leur interface doit être visible dans les interactions (et dans les diagrammes en général) ; sinon, les interactions (ou diagrammes) doivent être changés lorsque les sous-systèmes sont remplacés les uns par les autres.

Exemple :

L'interface ICoordinator est incluse dans le diagramme de séquence, mais pas le sous-système la fournissant :

Diagramme décrit dans le texte d'accompagnement.

L'envoi d'un message vers une ligne de vie de l'interface signifie que tout sous-système réalisant l'interface peut être remplacé pour l'interface dans le diagramme. Notez que la ligne de vie de l'interface ICoordinator n'émet pas de messages, puisque différents sous-systèmes réalisant l'interface peuvent envoyer différents messages. Cependant, si vous voulez décrire quels messages doivent être envoyés (ou sont autorisés à être envoyés) à partir de n'importe quel sous-système réalisant l'interface, de tels messages peuvent provenir de la ligne de vie de l'interface.

Comment travailler en parallèle

Dans certains cas, il peut être approprié de développer un sous-système de manière plus ou moins indépendante et en parallèle avec le développement d'autres sous-systèmes. Pour ce faire, vous devez tout d'abord trouver les dépendances du sous-système en identifiant les interfaces qui existent entre elles.

Il est possible d'effectuer le travail de la manière suivante :

  1. Concentrez-vous sur les exigences qui affectent les interfaces entre les sous-systèmes.
  2. Mettez en évidence les interfaces requises, en affichant les messages qui vont dépasser les limites du sous-système.
  3. Créez des diagrammes de séquence en termes de sous-système pour chaque cas d'utilisation.
  4. Détaillez les interfaces nécessaires pour fournir les messages.
  5. Développez chaque sous-système en parallèle et utilisez les interfaces en tant qu'instruments de synchronisation entre les équipes de développement.

Vous pouvez également choisir d'organiser les diagrammes de séquence en ce qui concerne les sous-systèmes ou uniquement leurs interfaces. Dans certains projets, il peut s'avérer nécessaire d'implémenter les classes fournissant les interfaces avant de continuer avec le reste de la modélisation.

Décrire le comportement lié à la persistance

L'objectif majeur du paradigme orienté objet est d'encapsuler les détails d'implémentation. C'est pourquoi, en ce qui concerne la persistance, nous aimerions obtenir un objet persistant ayant exactement la même apparence qu'un objet transitoire. Nous ne devrions pas avoir à prendre en compte le fait que l'objet soit persistant, ou à le traiter de manière différente d'un autre objet. C'est du moins l'objectif.

En pratique, il peut arriver que l'application ait besoin de contrôler différents aspects de persistance :

  • lorsque des objets persistants sont lus et écrits
  • lorsque des objets persistants sont supprimés
  • manière dont les transactions sont gérées
  • manière dont le contrôle du verrouillage et des accès concurrents est accompli

Ecrire des objets persistants

Deux cas sont concernés : la première fois où l'objet est écrit dans la mémoire de l'objet persistant, et les fois consécutives lorsque l'application veut mettre à jour la mémoire de l'objet persistant en prenant en compte un changement effectué sur l'objet.

Dans chaque cas, le mécanisme spécifique dépend des opérations prises en charge par la structure de persistance. Généralement, le mécanisme utilisé est d'envoyer un message à la structure de persistance pour créer l'objet persistant. Une fois que l'objet est persistant, la structure de persistance est suffisamment intelligente pour détecter les changements consécutifs affectant l'objet persistant et les écrit dans la mémoire de l'objet persistant, si nécessaire (en règle générale, lorsqu'une transaction est validée).

Un exemple de création d'un objet persistant est illustré ci-dessous :

Diagramme décrit dans le texte d'accompagnement.

L'objet PersistenceMgr est une instance de VBOS, une structure de persistance. OrderCoordinator crée un ordre persistant en l'envoyant en tant que paramètre effectif à un message 'createPersistentObject' vers PersistenceMgr.

Il n'est généralement pas nécessaire de modéliser ceci de manière explicite sauf s'il est important de savoir que l'objet est explicitement stocké à un point spécifique dans une certaine séquence d'événements. Si des opérations consécutives nécessitent d'émettre une requête sur l'objet, celui-ci doit exister dans la base de données, et c'est pourquoi, il est important de savoir que l'objet existe à cet endroit.

Lire des objets persistants

La récupération d'objets à partir de la mémoire de l'objet persistant est nécessaire avant que l'application ne puisse envoyer des messages à cet objet. Rappelez-vous que le travail dans un système orienté objet est réalisé par le biais d'envoi de messages aux objets. Mais si l'objet auquel vous voulez envoyer un message est dans la base de données mais pas encore en mémoire, un problème se posera : vous ne pouvez pas envoyer un message vers quelque chose qui n'existe pas encore!

Bref, vous devez envoyer un message à un objet sachant émettre une requête sur la base de données, récupérer l'objet correct et l'instancier. Ensuite, et seulement ensuite, vous pouvez envoyer le message prévu à l'origine. L'objet instanciant un objet persistant est souvent appelé objet fabrique. Un objet fabrique a pour rôle de créer des instances d'objets, y compris les objets persistants. En fonction d'une requête donnée, l'objet fabrique peut être conçu pour renvoyer un ensemble constitué d'un ou plusieurs objets correspondant à la requête.

De manière générale, les objets sont connectés les uns aux autres par l'intermédiaire de leurs associations, il est donc souvent uniquement nécessaire de récupérer l'objet racine dans un graphique d'objet ; le reste étant essentiellement 'extrait' de manière transparente de la base de données par les associations avec l'objet racine. (Un bon mécanisme de persistance est suffisamment intelligent pour ne récupérer que les objets dont on a besoin ; sinon, cela pourrait aboutir à instancier un grand nombre d'objets sans utilité. La récupération d'objets avant d'en avoir réellement besoin constitue l'un des problèmes de performance principaux qui résultent de mécanismes de persistance simplistes.)

L'exemple suivant illustre la manière de modéliser la récupération d'objet à partir de la mémoire d'objet persistant. Dans un diagramme de séquence réel, le SGBD ne serait pas affiché dans la mesure où il devrait être encapsulé dans l'objet fabrique.

Diagramme décrit dans le texte d'accompagnement.

Supprimer les objets persistants

Le problème des objets persistants, c'est leur persistance ! Contrairement aux objets transitoires qui disparaissent simplement lorsque le processus qui les a créés se termine, les objets persistants ne disparaissent que lorsqu'ils sont explicitement supprimés. Il est donc important de supprimer l'objet lorsqu'il n'est plus utilisé.

Le problème est que cela est difficile à déterminer. Ce n'est pas parce qu'une application est effectuée avec un objet que cela signifie que toutes les applications, présentes et futures, le seront. Et dans la mesure où les objets ont des associations qu'ils ne connaissent même pas, il n'est pas toujours facile de déterminer s'il est possible de supprimer un objet.

En conception, on peut représenter cela de manière sémantique en utilisant les diagrammes état-transition : lorsque l'objet se trouve à l'état final, il peut être considéré comme étant libéré. Les développeurs responsables de l'implémentation des classes persistantes peuvent ensuite utiliser les informations du diagramme état-transition pour appeler le comportement du mécanisme de persistance approprié afin de libérer l'objet. La responsabilité du concepteur de la réalisation de cas d'utilisation est d'appeler les opérations appropriées afin que l'objet puisse atteindre son état final lorsqu'il est approprié de supprimer l'objet.

Si un objet possède de nombreuses connexions vers d'autres objets, il peut s'avérer difficile de déterminer si l'objet peut être supprimé. Dans la mesure où un objet fabrique connaît la structure de l'objet ainsi que les objets auxquels il est connecté, il est souvent utile de donner la responsabilité à l'objet fabrique d'une classe de déterminer si une instance particulière peut être supprimée. La structure de persistance peut également fournir un support pour cette fonctionnalité.

Modélisation des transactions

Les transactions définissent un ensemble d'appels d'opérations qui sont atomiques; soit elles sont toutes effectuées, soit aucune d'entre elles n'est effectuée. Dans le contexte de la persistance, une transaction définit un ensemble de changements effectué sur un ensemble d'objets qui sont tous exécutés ou pas du tout exécutés. Les transactions permettent une cohérence, en garantissant que des ensembles d'objets se déplacent d'un état cohérent à l'autre.

Il existe différentes options permettant d'afficher les transactions dans les réalisations de cas d'utilisation :

  • Textuellement. A l'aide de scripts dans la marge du diagramme de séquence, les frontières de la transaction peuvent être documentées comme indiqué ci-dessous. Cette méthode est simple et permet d'utiliser un nombre indéfini de mécanismes pour implémenter la transaction.

Diagramme décrit dans le texte d'accompagnement.

Représenter les frontières de transaction à l'aide d'annotations textuelles.

  • Utilisation de messages explicites. Si le mécanisme de gestion de la transaction utilisé fait appel à des messages explicites pour commencer et finir les transactions, ces messages peuvent être affichés de manière explicite dans le diagramme de séquence, comme indiqué ci-dessous :

Diagramme décrit dans le texte d'accompagnement.

Diagramme de séquence affichant des messages explicites pour démarrer et arrêter les transactions.

Traiter les cas d'erreur

Si toutes les opérations spécifiées dans une transaction ne peuvent pas être effectuées (en règle générale en raison d'une erreur), la transaction est avortée et tous les changements effectués lors de la transaction sont inversés. Les cas d'erreur anticipés représentent souvent des flux d'événements exceptionnels dans les cas d'utilisation. Dans d'autres cas, les cas d'erreur se produisent en raison de certaines défaillances du système. L es cas d'erreur doivent également être documentés dans les interactions. Les erreurs et les exceptions simples peuvent être affichées dans l'interaction où elles se produisent ; les erreurs et les exceptions complexes peuvent nécessiter leurs propres interactions.

Il est possible d'afficher les modes de défaillance d'objets spécifiques sur les diagrammes état-transition. Le flux conditionnel de la gestion du contrôle de ces modes de défaillance peut être affiché dans l'interaction dans laquelle l'erreur ou l'exception se produit.

Gérer le contrôle des accès concurrents

L'accès concurrent décrit le contrôle d'accès vers les ressources système critiques au cours de la transaction. Afin de maintenir la cohérence du système, il est possible qu'une transaction nécessite d'avoir un accès exclusif à certaines ressources clés du système. Cette exclusivité peut inclure la possibilité de lire un ensemble d'objets, d'écrire un ensemble d'objets ou les deux.

Considérons un exemple simple illustrant la nécessité de restreindre l'accès à un ensemble d'objets. Supposons que nous exécutons un système d'entrée de commande simple. Les personnes appellent pour passer des commandes qui sont traitées puis expédiées. La commande peut être considéré comme une sorte de transaction.

Afin d'illustrer la nécessité d'un contrôle des accès concurrents, supposons le cas d'une commande d'une nouvelle paire de chaussures de randonnée. Lorsque la commande est entrée dans le système, celui-ci vérifie que les chaussures, à la bonne pointure, sont en stock. Si c'est le cas, nous souhaitons réserver cette paire, de telle manière que personne ne puisse les acheter avant l'expédition de la commande. Une fois la commande envoyée, les chaussures sont retirées du stock.

Entre le moment de la commande et celui de l'expédition, les chaussures se trouvent dans un état spécial(—), elles sont en stock, mais elles sont "réservées" par ma commande. Si ma commande est annulée pour quelque raison que ce soit (je change d'avis ou ma carte de crédit a expiré), les chaussures sont renvoyées vers le stock. Une fois la commande expédiée, nous supposons que notre petite société ne souhaite pas conserver l'enregistrement qu'elle possédait les chaussures.

L'objectif de l'accès concurrent, comme les transactions, est de s'assurer que le système passe d'un état cohérent à un autre. En outre, l'accès concurrent tend à assurer qu'une transaction dispose de toutes les ressources dont elle a besoin pour terminer son travail. Le contrôle des accès concurrents peut être implémenté de diverses manières, y compris le verrouillage des ressources, les sémaphores, les verrous de mémoire partagée et les espaces de travail privés.

Dans un système orienté objet, il est difficile de dire uniquement à partir des patterns d'un message si un message particulier peut provoquer un changement d'état d'un objet. De même, différentes implémentations peuvent parer à la nécessité de restreindre l'accès à un certain type de ressources ; par exemple, certaines implémentations fournissent chaque transaction avec sa propre vue de l'état du système au début de la transaction. Dans ce cas, d'autres processus peuvent changer l'état et l'objet sans affecter la 'vue' de toute autre transaction d'exécution.

Afin d'éviter d'imposer des contraintes à l'implémentation, en conception, nous indiquons simplement les ressources auxquelles la transaction doit avoir un accès exclusif. En reprenant l'exemple cité auparavant, nous indiquons que nous avons besoin d'un accès exclusif à la paire de chaussures commandée. Une alternative simple consiste à annoter la description du message envoyé, en indiquant que l'application a besoin d'un accès exclusif vers l'objet. L'implémenteur peut ensuite utiliser ces informations afin de déterminer le meilleur moyen d'implémenter l'exigence d'accès concurrent. L'exemple ci-dessous illustre l'annotation d'un message nécessitant un accès exclusif. On part du principe que tous les verrouillages sont relâchés et que la transaction est terminée.

Diagramme décrit dans le texte d'accompagnement.

Exemple illustrant le contrôle d'accès annoté dans un diagramme de séquence.

La raison qui incite à de ne pas restreindre l'accès à tous les objets nécessaires dans une transaction résulte souvent du fait que seuls quelques objets ont des restrictions d'accès ; restreindre l'accès à tous les objets participant à une transaction gaspille de précieuses ressources et peut créer des goulots d'étranglement en matière de performance plutôt que de les empêcher.

Affiner la description du flux d'événements

Dans le flux d'événements de la réalisation de cas d'utilisation, il est possible que vous ayez besoin d'ajouter une description supplémentaire aux diagrammes de séquence, au cas où l'examen des messages envoyés entre les objets participants ne suffit pas à éclaircir complètement le flux d'événements. Quelques exemples de ces cas comprennent les cas où des annotations de temps, des remarques sur le comportement conditionnel ou une clarification du comportement de l'opération sont nécessaires pour faciliter la lecture des diagrammes par des observateurs externes.

Le flux d'événements est mis en évidence dans la Tâche : Analyse de cas d'utilisation. Au cours de cette étape, vous affinez le flux d'événements afin de rendre les diagrammes de séquence plus clairs.

Souvent, le nom de l'opération n'est pas suffisant pour comprendre pourquoi elle est exécutée. Des notes sous forme de texte ou des scripts dans la marge du diagramme peuvent s'avérer nécessaires pour rendre le diagramme de séquence plus clair. Ils peuvent également être nécessaires pour représenter le flux de contrôle comme les étapes de décision, le bouclage et l'embranchement. En outre, il est possible que des balises textuelles soient nécessaires pour corréler les points d'extension dans le cas d'utilisation avec les emplacements spécifiques des diagrammes de séquence.

Les exemples précédents de cette tâche ont illustré un nombre différent de méthodes d'annotation de diagrammes de séquence.



Unifier les classes et les sous-systèmes de conception

Lorsque les cas d'utilisation sont réalisés, vous devez unifier les classes et les sous-systèmes de conception identifiées afin d'assurer l'homogénéité et la cohérence du modèle de conception.

Points à prendre en compte :

  • Les noms des éléments de modèle doivent décrire leur fonction.
  • Eviter des noms similaires et des synonymes dans la mesure où cela rend difficile la distinction des éléments de modèle.
  • Fusionner les éléments de modèle définissant un comportement similaire ou représentant le même phénomène.
  • Fusionner les classes d'entité représentant le même concept ou ayant les mêmes attributs, même si leur comportement défini est différent.
  • Utilisez l'héritage pour rendre les éléments de modèle plus abstraits, ce qui tend à rendre le modèle plus robuste.
  • Lors de la mise à jour d'un élément de modèle, mettez également à jour la description du flux d'événements correspondante des réalisations de cas d'utilisation.
Evaluation de vos résultats

A ce stade, vous devez évaluer le modèle de conception afin de vérifier que votre travail va dans la bonne direction. Il n'est pas nécessaire d'examiner le modèle en détail, mais vous devez prendre en considération le Modèle de conception lorsque vous travaillez dessus.

Référez-vous en particulier à la Réalisation de cas d'utilisation dans la Tâche: Réviser la conception.

Plus d'informations