Activité :
|
Détail de l'enchaînement d'activités : |
Les classes sont les chevilles ouvrières de l'activité de conception : elles réalisent le travail véritable dans le système. D'autres éléments de conception, comme les sous-systèmes, packages et collaborations, décrivent le regroupement ou l'interopération des classes.
Les classes actives sont des classes de conception qui coordonnent et dirigent le comportement des classes passives : une classe active est une classe dont les instances sont des objets actifs, propriétaires de leurs propres fils de contrôle.
Utilisez les patterns et les mécanismes de conception comme approprié à la classe ou à la capacité à développer, et en conformité avec les principes de conception du projet.
L'incorporation d'un pattern ou d'un mécanisme a pour effet de réaliser concrètement plusieurs des étapes ultérieures de cette activité (ajout de nouvelles classes, d'opérations, d'attributs et de relations) mais en conformité avec les règles définies par le pattern ou le mécanisme.
Notez que les patterns et mécanismes sont généralement incorporés au fur et à mesure de l'évolution de la conception et non pas en tant que première étape de cette activité. Ils sont aussi fréquemment appliqués à un groupe de classes, plutôt qu'à une classe unique.
Créez une ou plusieurs classes de conception initiales pour la classe d'analyse fournie comme entrée de cette activité et affectez-leur des dépendances de trace. Les classes de conception créées lors de cette étape seront affinées, ajustées, fractionnées ou fusionnées au cours d'étapes ultérieures lorsque diverses propriétés de conception décrivant la conception de la classe d'analyse leur seront affectées (comme des opérations, méthodes et un automate à états).
Selon le type de classe d'analyse (limite, entité ou contrôle) envisagé, vous pouvez utiliser des stratégies spécifiques pour créer vos classes de conception initiales.
Les classes limites représentent des interfaces avec des utilisateurs ou avec d'autres systèmes.
Généralement, les classes limites représentant des interfaces avec d'autres systèmes sont modélisées en tant que sous-systèmes vu qu'elles comportent souvent un comportement interne complexe. Si le comportement de l'interface est simple (agissant par exemple uniquement comme passe-système vers une API existante du système externe), vous pouvez choisir de la représenter à l'aide d'une ou de plusieurs classes de conception. Dans ce cas, utilisez une classe de conception unique par protocole, interface, ou API, et notez les exigences spéciales concernant les normes utilisées dans les exigences spéciales de la classe.
Les classes limites représentant des interfaces vers les utilisateurs suivent généralement la règle selon laquelle une classe est dédiée à chaque fenêtre ou à chaque formulaire de l'interface utilisateur. Par conséquent, les responsabilités des classes limites peuvent être définies à un niveau relativement abstrait et doivent être élaborées et détaillées dans cette étape. Vous pouvez aussi envisager d'utiliser d'autres modèles et prototypes d'interface utilisateur dans cette étape.
La conception des classes limites dépend des outils de développement de l'interface utilisateur disponibles pour le projet. A l'aide des technologies actuelles, il est courant que l'interface utilisateur soit construite visuellement, directement dans l'outil de développement. Des classes de l'interface utilisateur sont ainsi créées automatiquement et doivent être associées à la conception des classes de contrôle et d'entités. Si l'environnement de développement de l'interface utilisateur crée automatiquement les classes de support requises pour implémenter cette interface, vous n'avez pas en prendre en compte ces classes pour la conception. Vous n'avez à concevoir que les éléments qui ne sont pas créés pour vous par l'environnement de développement.
Les classes d'entités représentent les unités d'information manipulées lors de l'analyse. Elles sont souvent passives et persistantes et peuvent être identifiées et associées au mécanisme d'analyse pour persistance. Les rouages de la conception d'un mécanisme de persistance basée base de données sont couverts dans la section Activité : Conception de la base de données. Des considérations de performances peuvent vous forcer à effectuer un réusinage des classes persistantes, provoquant des modifications du modèle de conception (discutées conjointement dans Rôle : Concepteur de base de données et Rôle : Concepteur).
Une discussion plus approfondie des questions de conception pour les classes persistantes est présentée plus loin sous la rubrique Identification des classes persistantes.
Un objet de contrôle est responsable de la gestion du flux d'un cas d'utilisation et, par conséquent, coordonne la plupart de ses actions. Les objets de contrôle encapsulent la logique qui n'est pas directement associée aux aspects de l'interface utilisateur (objets limites) ou d'ingénierie des données (objets entités). Cette logique est parfois dénommée logique applicative ou logique métier.
Prenez en compte les aspects suivants lors de la conception de classes de contrôle :
- Le comportement de coordination du cas d'utilisation devient imbriqué dans l'interface utilisateur, rendant plus difficile toute modification du système.
- La même interface utilisateur ne peut pas être utilisée sans difficultés dans des réalisations de cas d'utilisation différentes.
- L'interface utilisateur est alourdie par des fonctionnalités supplémentaires, ce qui dégrade ses performances.
- Les objets entités peuvent être grevés par un comportement spécifique à un cas d'utilisation, ce qui réduit leur généralité.
Pour éviter ces problèmes, des classes de contrôle sont introduites afin de fournir le comportement associé à la coordination des flux d'événements.
Dans les deux derniers cas, si la classe de contrôle représente un fil de contrôle séparé, il peut être préférable d'utiliser une classe active pour modéliser le fil de contrôle.
Les classes devant stocker leur état sur un support permanent sont dénommées persistantes. Cette mémorisation de leur état peut répondre à un besoin d'enregistrement permanent des informations de classe, de sauvegarde en cas d'échec du système, ou d'échange d'informations. Une classe persistante peut comporter à la fois des instances persistantes et temporaires. La désignation d'une classe en tant que telle signifie simplement que certaines instances de la classe peuvent devoir être persistantes.
Incorporez les mécanismes de conception correspondant aux mécanismes de persistance détectés lors de l'analyse. En fonction des besoins de la classe, le mécanisme d'analyse de la persistance pourrait par exemple être réalisé par l'un des mécanismes de conception suivants :
Les objets persistants peuvent ne pas être dérivés seulement des classes d'entités. Ces objets peuvent aussi être requis pour traiter des exigences non fonctionnelles générales. Il peut s'agir, par exemple, d'objets persistants servant à conserver les informations relatives au contrôle de processus ou les informations d'état entre transactions.
L'identification des classes persistantes sert à aviser le Rôle : Concepteur de base de données que les caractéristiques de stockage physique de la classe requièrent une attention spéciale. Elle informe également le Rôle : Architecte logiciel que la classe doit être persistante et le Rôle : Concepteur responsable du mécanisme de persistance que les instances de la classe doivent être rendues persistantes.
En raison de la nécessité d'une stratégie de persistance coordonnée, le Rôle : Concepteur de base de données est responsable du mappage des classes persistantes dans la base de données, à l'aide d'une structure de persistance. Si le projet est amené à développer une structure de persistance, son développeur aura aussi à charge de déterminer les exigences des classes de conception en matière de persistance. Il suffit à ce stade, pour que ces personnes disposent des informations requises, d'indiquer que la classe est persistante, ou, plus exactement, que les instances de la classe le sont.
Définissez la visibilité de chaque classe dans le package où elle réside. Une classe publique peut être référencée en dehors du package qui la contient. Une classe privée (ou dont la visibilité est définie à implémentation) ne peut être référencée que par des classes du même package.
Pour identifier les opérations sur les classes de conception, procédez comme suit :
Des opérations sont requises pour prendre en charge les messages apparaissant dans les diagrammes de séquence car des scripts (spécifications de messages temporaires n'ayant pas encore été affectées aux opérations) décrivent le comportement que la classe est censée concrétiser. Un exemple de diagramme de séquence est illustré à la Figure 1.
Figure 1 : Les messages forment la base de l'identification des opérations
Les réalisations de cas d'utilisation ne peuvent pas fournir assez d'informations pour identifier toutes les opérations. Pour déterminer les opérations restantes, posez-vous les questions suivantes :
Ne définissez pas d'opérations se contentant d'acquérir et de définir les valeurs d'attributs publics (voir Définition d'attributs et Définition d'associations ). Elles sont habituellement générées par des utilitaires de génération de code et n'ont pas à être définies explicitement.
Utilisez les conventions d'attribution de noms du langage d'implémentation lorsque vous devez nommer des opérations, des types de retours, des paramètres et leurs types.
Pour chaque opération, vous devez définir les points suivants :
Après avoir défini les opérations, complétez les diagrammes de séquence avec les informations sur les opérations appelées pour chaque message.
Pour plus d'informations, reportez-vous à la section intitulée Opérations de classes dans Principes et conseils : Classe de conception.
Définissez la visibilité à l'exportation de chaque opération d'après l'une des options suivantes :
Sélectionnez la visibilité la plus restreinte possible pouvant accomplir les objectifs de l'opération. Pour ce faire, observez les diagrammes de séquence et, pour chaque message, déterminez s'il provient d'une classe hors du package du récepteur (requiert alors une visibilité publique), à l'intérieur du package (requiert une visibilité implémentation), d'une sous-classe (requiert une visibilité protégée), ou de la classe elle-même ou d'une classe amie (requiert une visibilité privée).
Pour la plupart, les opérations sont des opérations d'instance, c'est-à-dire qu'elles sont effectuées sur des instances de la classe. Dans certains cas cependant, une opération s'applique à toutes les instances de la classe et constitue alors une opération avec portée sur la classe. Le récepteur de l'opération de classe est en fait une instance d'une métaclasse (la description de la classe elle-même) au lieu d'une instance spécifique de la classe. Les opérations de classe incluent les messages qui créent (instancient) de nouvelles instances, qui retournent toutes les instances d'une classe (allInstances) et ainsi de suite .
La chaîne d'opération est soulignée pour dénoter qu'il s'agit d'une opération avec portée sur la classe.
Une méthode spécifie l'implémentation d'une opération. Dans les nombreux cas où le comportement requis par l'opération est suffisamment défini par son nom, sa description et ses paramètres, les méthodes sont implémentées directement dans le langage de programmation. Lorsque l'implémentation d'une opération nécessite l'utilisation d'un algorithme ou des informations absentes de sa description, une description de méthode séparée est requise. La méthode ne décrit pas seulement ce que fait l'opération mais aussi comment elle s'y prend.
Si elle est décrite, la méthode doit expliquer :
Les exigences varient d'un cas à l'autre. Cependant, les spécifications d'une méthode pour une classe doivent toujours stipuler :
Des exigences plus spécifiques peuvent détailler les points suivants :
Les diagrammes de séquence sont une source précieuse pour cette description. Ces diagrammes élucident les opérations qui seront utilisées dans d'autres objets lorsqu'une opération est effectuée. Cette spécification est nécessaire pour l'implémentation complète d'une opération. La production d'une spécification de méthode complète requiert, par conséquent, d'identifier les opérations des objets impliqués et d'examiner les diagrammes de séquence correspondants.
Le comportement de certaines opérations est fonction de l'état de leur objet récepteur. Un automate à états est un outil qui décrit les états pouvant être assumés par un objet et les événements conduisant cet objet à passer à un autre état (voir Principes et conseils : Diagrammes d'état-transition). Les automates à états sont particulièrement utiles pour décrire des classes actives.
La Figure 2 propose un exemple d'automate à états simple.
Figure 2 : Diagramme d'état-transition simple pour un distributeur de carburant
Chaque événement de transition d'état peut être associé à une opération. L'opération peut avoir un comportement différent selon l'état de l'objet et les événements de transition expliquent comment ceci se produit.
La description de la méthode pour l'opération associée doit être mise à jour avec les informations d'état spécifiques, indiquant pour chaque état pertinent ce que doit accomplir l'opération. Les états sont souvent représentés à l'aide d'attributs ; les diagrammes d'état-transition servent d'entrée pour l'étape d'identification d'attribut.
Pour plus d'information, voir Principes et conseils : Diagramme d'état-transition.
Les attributs requis par la classe pour réaliser ses opérations sont déterminés lors de la définition des méthodes et l'identification des états. Les attributs assurent le stockage des informations de l'instance de classe et sont souvent utilisés pour représenter son état. Les informations conservées par la classe elle-même sont préservées à l'aide de ses attributs. Vous devez définir pour chaque attribut :
Vérifiez que tous les attributs sont effectivement requis. Les attributs doivent être justifiés : il est possible que certains aient été ajoutés au début du processus et conservés bien qu'ils n'aient plus d'utilité, par manque de perspective. Les attributs superflus, multipliés par des milliers ou des millions d'instances, peuvent avoir un effet négatif sur les performances et les besoins de stockage d'un système.
Pour plus d'informations sur ce sujet, reportez-vous à la section intitulée Attributs dans Principes et conseils : Classe de conception.
Pour chaque cas où la communication entre objets est requise, élucidez les questions suivantes :
Notez que les liens modélisés de cette manière sont transitoires (n'existent que pour une durée limitée et dans le contexte spécifique de la collaboration) et sont, en ce sens, des instances du rôle d'association dans la collaboration. Cependant, la relation dans un modèle de classes (c'est-à-dire indépendant du contexte) doit constituer une dépendance, comme mentionné auparavant. Comme stipulé dans [RUM98], dans la définition de lien transitoire : "Il est possible de modéliser tous les liens de ce type en tant qu'associations, auquel cas les conditions affectant les associations doivent être énoncées de manière très générale et au prix d'une perte de précision importante quant aux contraintes affectant les combinaisons d'objets." Dans cette situation, la modélisation d'une dépendance est moins importante que celle de la relation dans la collaboration, étant donné que la dépendance ne décrit pas complètement la relation mais seulement son existence.
Les associations fournissent un mécanisme permettant aux objets de communiquer entre-eux. Elles fournissent aux objets un conduit où les messages peuvent circuler. Elles documentent également les dépendances entre classes, soulignant que les modifications apportées à une classe devraient être ressenties dans de nombreuses autres classes.
Examinez les descriptions de méthode de chaque opération afin de comprendre comment les instances de la classe communiquent et collaborent avec d'autres objets. Pour envoyer un message à un autre objet, un objet doit disposer d'une référence au récepteur du message. Un diagramme de communication (représentation alternative d'un diagramme de séquence) présente les communications de l'objet en termes de liens, comme illustré à la Figure 3.
Figure 3 : Exemple de diagramme de communication
Les messages restants utilisent une association, ou bien une agrégation, pour spécifier la relation entre les instances de deux classes qui communiquent. Voir Principes et conseils : Association et Principes et conseils : Agrégation pour plus d'indications sur la sélection de la représentation adéquate. Pour ces deux associations, définissez la visibilité du lien à zone dans les diagrammes de communication. Il convient ensuite de compléter les tâches suivantes :
Il est préférable de définir les associations et les agrégations dans un diagramme de classes décrivant les classes associées. Ce diagramme doit être la propriété du package contenant les classes associées. La Figure 4 illustre un exemple d'un tel diagramme, décrivant les associations et agrégations.
Figure 4 : Exemple de diagramme de classe présentant les associations, agrégations et généralisations entre classes
Les associations de souscription entre classes d'analyse sont utilisées pour identifier des dépendances d'événements entre classes. Dans le modèle de conception, vous devez traiter ces dépendances d'événements explicitement, soit en utilisant les structures de gestionnaires d'événements disponibles, soit en concevant et en créant la votre. Sous certains langages de programmation (comme Visual Basic), ceci ne présente pas de difficulté particulière : vous n'avez qu'à déclarer, invoquer et gérer les événements correspondants. Sous d'autres langages, vous pouvez devoir utiliser une bibliothèque complémentaire de fonctions réutilisables pour gérer les souscriptions et les événements. Si vous ne pouvez pas acquérir la fonctionnalité requise, vous devrez la concevoir et la construire. Voir aussi Principes et conseils : Association de souscription.
Certaines classes peuvent représenter des abstractions complexes et comporter une structure complexe. Lors de la modélisation d'une classe, le concepteur peut vouloir représenter ses éléments internes participant et leurs relations pour s'assurer que l'implémenteur implémente en conséquence les collaborations intervenant à l'intérieur de cette classe.
Dans le langage UML 2.0, les classes sont définies en tant que classes structurées , pouvant disposer d'une structure interne et de ports. Les classes peuvent donc être décomposées en collections d'éléments connectés qui pourront à leur tour être décomposées plus avant. Une classe peut être encapsulée, en forçant les communications venant de l'extérieur à passer par des ports respectant les interfaces déclarées.
En présence d'une classe et d'une structure complexes, crée un diagramme de structure composite pour cette classe. Modélisez les éléments qui rempliront les rôles afférents au comportement de la classe. Déterminez comment les parties sont 'raccordées' par le biais de connecteurs. Faites usage de ports avec interfaces déclarées si vous voulez permettre à des clients différents un accès à des portions spécifiques du comportement offert par cette classe. Utilisez également des ports afin d'isoler complètement de son environnement les parties internes de cette classe.
Pour plus d'informations sur ce sujet et pour des exemples de diagramme de structure composite, voir Concepts : Classe structurée.
Les classes peuvent être organisées sous une hiérarchie de généralisations de sorte à refléter les comportements communs et la structure commune. Une superclasse commune peut être définie, dont des sous-classes hériteront à la fois le comportement et la structure. La généralisation est une convenance de notation vous permettant de définir à un endroit une structure et un comportement communs, et de la réutiliser là où ce comportement et cette structure se reproduisent. Pour plus d'informations sur les relations de généralisation, reportez-vous à la rubrique Principes et conseils : Généralisation .
Lorsque vous identifiez une généralisation, définissez une superclasse commune qui contiendra les attributs, associations, agrégations et opérations communs. Détachez le comportement commun des classes destinées à devenir des sous-classes de la superclasse commune. Définissez une relation de généralisation de la sous-classe envers la superclasse.
L'objectif de cette étape est d'éviter des conflits d'accès concurrent pouvant émerger lorsque deux cas d'utilisation, ou plus, sont susceptibles d'accéder simultanément et de manière contradictoire à des instances de la classe de conception.
L'une des difficultés à progresser dans le processus de conception pas à pas, un cas d'utilisation après l'autre, provient du fait que deux cas d'utilisation (ou plus) pourraient tenter d'appeler simultanément et de manière contradictoire des opérations sur des objets de la conception. Dans de tels cas, les conflits d'accès concurrent doivent être identifiés et résolus explicitement.
Si un échange de messages synchrone est utilisé, l'exécution d'une opération bloquera les appels ultérieurs aux objets jusqu'à ce que l'opération se termine. L'échange de messages synchrone implique un traitement des messages selon le principe premier arrivé, premier servi. Ceci peut résoudre un conflit d'accès concurrent, notamment dans les cas où tous les messages ont la même priorité ou dans les cas où chaque message s'exécute dans le même fil d'exécution. Dans les cas où plusieurs fils d'exécution (représentés par des classes actives) sont susceptibles d'accéder à un même objet, des mécanismes explicites doivent être utilisés pour empêcher ou pour résoudre le conflit d'accès concurrent.
Il se peut que des opérations différentes sur le même objet soient appelées simultanément par divers fils d'exécution sans pour autant provoquer un conflit d'accès concurrent ; ainsi, le nom et l'adresse d'un client pourraient être modifiés simultanément sans générer de conflit. Un conflit ne surgit que lorsque deux fils d'exécution différents tentent de modifier la même propriété de l'objet.
Pour chaque objet auquel des fils d'exécution différents pourraient avoir un accès concurrent, identifiez les sections du code devant être protégés contre un accès simultané. Au début de la phase d'élaboration, l'identification de segments de code spécifiques ne sera pas possible ; il suffit à ce stade de détecter les opérations à protéger. Sélectionnez (ou concevez) ensuite des mécanismes de contrôle d'accès adéquats pour empêcher des accès simultanés conflictuels. Parmi ces mécanismes, vous pouvez envisager d'inclure une mise en file d'attente des messages afin de sérialiser les accès, d'utiliser des sémaphores ou des jetons afin de n'autoriser l'accès qu'à un seul fil à la fois, ou encore envisager d'autres variantes de mécanismes de verrouillage. Le choix du mécanisme est conditionné dans une large mesure par l'implémentation et varie habituellement avec le langage de programmation et l'environnement d'exploitation.
Les classes de conception sont affinées afin de se prêter aux exigences générales, non fonctionnelles. Un apport important dans cette étape provient des exigences non fonctionnelles sur une classe d'analyse qui peuvent déjà avoir été stipulées dans ses exigences spéciales et responsabilités. Ce type d'exigences est souvent spécifié sous l'angle des mécanismes d'architecture (analyse) requis pour réaliser la classe. Au cours de cette étape, la classe est ensuite optimisée en intégrant les mécanismes de conception correspondant à ces mécanismes d'analyse.
Les mécanismes de conception disponibles sont identifiés et caractérisés par l'architecte logiciel . Pour chaque mécanisme de conception requis, qualifiez autant de caractéristiques que possible, en spécifiant des plages de valeurs le cas échéant. Pour plus d'informations sur ces mécanismes, reportez-vous aux rubriques Activité : Identification des mécanismes de conception, Concepts : Mécanismes d'analyse, et Concepts : Mécanismes de conception et d'implémentation.
Divers principes et mécanismes généraux de conception peuvent devoir être pris en compte pour la conception des classes, par exemple en ce qui concerne :
Vérifiez à ce stade le modèle de conception pour confirmer que vos efforts sont orientés dans la bonne direction. Il est inutile d'examiner le modèle dans le détail, mais prêtez toutefois attention aux points de contrôle suivants :
RUP (Rational Unified Process)
|