Instructions: Classe de conception
Une classe de conception représente un élément de conception qui sera directement mappé à un code. Ces instructions expliquent comment développer une classe de conception.
Relations
Description principale

Définition

Une classe de conception est une abstraction d'une ou plusieurs classes dans l'implémentation du système ; ce à quoi elle correspond exactement dépend du langage d'implémentation. Par exemple, dans un langage orienté objet comme C++, une classe peut correspondre à une classe simple. Dans le langage Ada, une classe peut correspondre à un type référencé défini dans la partie visible du package.

Les classes définissent des objets, qui à leur tour créent (implémentent) les cas d'utilisation. Une classe est créée en fonction des exigences que les cas d'utilisation ont vis à vis des objets dont le système a besoin, mais aussi des modèles d'objet développés antérieurement.

Le fait qu'une classe soit bonne ou non dépend en grande partie de l'environnement de l'implémentation. Par exemple, la taille appropriée de la classe et des ses objets dépend du langage de programmation. Ce qui peut être exact en utilisant Ada ne l'est pas forcément en utilisant Smalltalk. Les classes doivent être mappées à un phénomène particulier dans le langage d'implémentation et être structurées de façon à ce que le mappage soit dans le code approprié.

Même si les caractéristiques du langage d'implémentation influencent le modèle de conception, la structure de la classe doit rester facile à comprendre et à modifier. Lors de la conception, faites comme si vous aviez des classes et des encapsulations, même si le langage d'implémentation ne les prend pas en charge.

Opérations

La seule façon dont des objets peuvent accéder ou affecter les attributs ou les relations d'un autre objet est au travers de leurs opérations. Les opérations d'un objet sont définies par sa classe. Un comportement particulier peut être réalisé par le biais d'opérations, ce qui peut affecter les attributs et les relations de l'objet et entraîner la réalisation d'autres opérations. Une opération correspond à une fonction membre dans C++ et à une fonction ou une procédure dans Ada. Le comportement que vous attribuez à un objet dépend du rôle qu'il a dans la réalisation des cas d'utilisation.

Paramètres

Lors de la spécification d'une opération, les paramètres constituent des paramètres formels. Chaque paramètre a un nom et un type. Vous pouvez utiliser la syntaxe et la sémantique du langage d'implémentation pour définir les opérations et leurs paramètres. De cette façon, ils seront déjà définis dans le langage d'implémentation lorsque la programmation commencera.

Exemple :

Dans le système d'une machine de recyclage, les objets de la classe réception gardent une trace des articles consignés d'un certain type qu'un client a déposés. Le comportement d'un objet réception inclut le fait d'incrémenter le nombre d'objets rapportés. L'opération insérer un article, qui reçoit la référence de l'objet déposé, remplit cette fonction.

Diagramme décrit dans le texte d'accompagnement.

Utilisez la syntaxe et la sémantique du langage d'implémentation lors de la définition des opérations.

Opérations de classe

Une opération est quasiment toujours relative au comportement de l'objet. Une opération peut aussi être relative au comportement d'une classe. Dans ce cas, c'est une opération de classe. Cela peut être modélisé en langage UML en définissant le type de l'opération.

Visibilité de l'opération

Voici les différentes visibilités d'une opération :

  • Publique : l'opération est visible pour modéliser d'autres éléments que la classe.
  • Protégée : l'opération n'est visible que pour la classe et ses sous-classes ou pour des amis de la classe (dépendant du langage)
  • Privée : l'opération n'est visible que pour la classe et pour les amis de la classe
  • Implémentation : l'opération n'est visible qu'à l'intérieur de la classe.

La visibilité publique doit être utilisée de façon très rare, seulement lorsqu'une autre classe a besoin d'une opération.

La visibilité protégée doit être la visibilité par défaut ; elle protège l'opération des classes externes, ce qui favorise le couplage lâche et l'encapsulation de comportement.

La visibilité privée doit être utilisée lorsque vous voulez éviter que les sous-classes héritent de l'opération. C'est un moyen de découpler les sous-classes des superclasses et de réduire le besoin de supprimer ou d'exclure les opérations héritées non-utilisées.

La visibilité implémentation est la plus restrictive ; elle ne doit être utilisée que dans les cas où seule la classe est capable d'utiliser l'opération. C'est une variante de la visibilité privée, qui elle convient dans la plupart des cas.

Etats

Un objet peut réagir différemment à un message spécifique selon l'état dans lequel il se trouve ; le comportement état-dépendant d'un objet est défini par un diagramme d'état-transition associé. Pour chaque état que l'objet peut entrer, le diagramme d'état-transition décrit les messages qu'il peut recevoir, les opérations qui seront effectuées et l'état dans lequel l'objet sera après. Voir aussi Technique : diagramme d'état-transition pour plus d'informations.

Collaborations

Une collaboration est un ensemble dynamique d'interactions d'objets dans lequel plusieurs objets communiquent en s'envoyant des messages. Envoyer un message se fait de façon directe dans Smalltalk ; dans Ada, cela s'effectue sous la forme d'un appel de sous-programme. Un message est envoyé à un objet récepteur qui appelle une opération à l'intérieur de l'objet. Le message indique le nom de l'opération à accomplir ainsi que les paramètres requis. Lorsqu'un message est envoyé, les paramètres effectifs (les valeurs des paramètres formels) sont fournis pour tous les paramètres.

Les transmissions des messages entre les objets dans la réalisation d'un cas d'utilisation et le focus de contrôle des objets suivent les appels des opérations et sont décrits dans un diagramme d'interaction. Voir aussi Technique: diagramme de séquence et Technique: diagramme de communication pour plus d'informations sur ces diagrammes.

Un attribut est la propriété nommée d'un objet. Le nom de l'attribut décrit le rôle de l'attribut en relation avec l'objet. Un attribut peut avoir une valeur initiale lorsque l'objet est créé.

Vous devez modéliser des attributs seulement si cela rend les objets plus compréhensibles. Il faut modéliser la propriété d'un objet comme un attribut seulement si c'est la propriété de cet objet uniquement. Sinon, vous pouvez modéliser la propriété avec une relation d'association ou d'agrégation à une classe dont les objets représentent la propriété.

Exemple :

Diagramme décrit dans le texte d'accompagnement.

Exemple de la façon dont un attribut est modélisé. Chaque membre d'une famille a un prénom et une adresse. Ici, nous avons identifié les attributs mon prénom et mon adresse respectivement comme étant de type Prénom et Adresse :

Diagramme décrit dans le texte d'accompagnement.

Dans cet exemple, une association est utilisée à la place d'un attribut. La propriété mon prénom est probablement propre à chaque membre de la famille. Nous pouvons donc le modéliser comme un attribut du type Prénom. Une adresse, en revanche, est partagée par tous les membres d'une famille. Elle est donc mieux modélisée par une association entre la classe membre de la famille et la classe adresse.

Ce n'est pas toujours facile de décider immédiatement de modéliser un concept en tant qu'objet séparé ou en tant qu'attribut d'un autre objet. Le fait d'avoir des objets inutiles dans le modèle d'objet entraîne une documentation inutile et augmente le temps système. Vous devez donc établir des critères permettant de déterminer l'importance d'un concept pour le système.

  • Accessibilité. Ce qui vous amène à choisir entre un objet et un attribut n'est pas l'importance du concept dans la vie réelle, mais le besoin d'y accéder pendant le cas d'utilisation. Si on accède régulièrement à l'unité, modélisez-la comme un objet.
  • Séparation pendant l'exécution. Modélisez les concepts traités séparément pendant l'exécution des cas d'utilisation comme des objets.
  • Liens avec les autres concepts. Modélisez les concepts étroitement liés à d'autres concepts et jamais utilisés séparément, mais toujours par le biais d'un objet, comme un attribut de l'objet.
  • Demandes de relations. Si vous devez, pour une raison quelconque, établir un rapport ente une unité et deux directions, ré-examinez l'unité pour voir si elle doit être un objet séparé. Deux objets ne peuvent associer la même instance d'un type d'attribut.
  • Fréquence d'occurrence. Si une unité existe seulement pendant un cas d'utilisation, ne la modélisez pas en tant qu'objet. Modélisez-la comme un attribut de l'objet qui effectue le comportement en question ou mentionnez-la dans la description de l'objet affecté.
  • Complexité. Si un objet devient trop complexe à cause de ses attributs, vous pouvez extraire certains de ses attributs pour en faire des objets séparés. Cependant, ne le faites pas trop souvent pour ne pas avoir trop d'objets. Les unités peuvent aussi être directes. Par exemple, sont considérées comme des attributs : (1) Les unités assez simples pour être prises en charge par les types primitifs du langage d'implémentation, comme les entiers dans C++ et (2) les unités assez simples pour être implémentées en utilisant les composants indépendants de l'application de l'environnement d'implémentation, comme les chaînes dans C++ et Smalltalk-80.

Un concept sera souvent modélisé différemment selon les systèmes. Dans un système, un concept sera si important que vous le modéliserez en tant qu'objet. Dans un autre, son importance sera relative et vous le modéliserez en tant qu'attribut d'un objet.

Exemple :

Par exemple, vous devez mettre au point un système gérant les départs pour une compagnie aérienne.

Diagramme décrit dans le texte d'accompagnement.

Un système gérant les départs. Supposons que le personnel de l'aéroport veuille un système qui gère les départs. Pour chaque départ, vous devez préciser l'heure de départ, la compagnie aérienne et la destination. Vous pouvez modéliser cela comme l'objet d'une classe départ, avec les attributs heure de départ, compagnie, et destination.

Si le système doit être mis au point pour une agence de voyage, la situation peut être quelque peu différente.

Diagramme décrit dans le texte d'accompagnement.

Les destinations des vols sont un objet à part entière, destination.

Bien sûr, on aura toujours besoin de l'heure de départ, de la compagnie aérienne et de la destination. Mais il y aura aussi d'autres exigences. Par exemple, une agence de voyage souhaite trouver des départs pour des destinations particulières. Vous devez donc créer un objet séparé pour destination. Les objets départ et destination doivent, évidemment, avoir conscience l'un de l'autre, ce que permet une association entre leurs classes.

L'argument concernant l'importance de certains concepts est aussi applicable pour déterminer les attributs devant être définis dans une classe. La classe voiture définira, de toutes évidences, des attributs différents si ses objets font partie d'un système de référencement des véhicules motorisés ou d'un système de fabrication de voitures.

En réalité, les règles définissant ce qu'il faut représenter comme des objets et ce qu'il faut représenter comme des attributs ne sont pas absolues. En théorie, tout peut être modélisé sous forme d'objet, mais cela prend trop d'espace. Une méthode simple consiste à considérer un objet comme une chose qui, à un moment donné, est utilisée sans tenir compte des autres objets. De plus, vous n'avez pas à modéliser chaque propriété de l'objet en utilisant un attribut, mais seulement les propriétés nécessaires à la compréhension de l'objet. Mieux vaut ne pas modéliser les détails trop spécifiques à une implémentation : ils sont mieux traités par le descripteur.

Attributs de classe

Un attribut est presque toujours relatif aux propriétés d'un objet. Un attribut peut aussi être relatif aux propriétés d'une classe auquel cas c'est un attribut de classe. Cela peut être modélisé en langage UML en définissant le type de l'attribut.

Modélisation des unités externes à l'aide d'attributs

Un objet peut encapsuler quelque chose dont la valeur peut changer sans que l'objet n'exécute un comportement. Cela peut être une unité externe qui n'a pas été modélisée comme un acteur. Par exemple, les frontières d'un système peuvent avoir été choisies de façon à ce qu'une sorte d'équipement de détection se trouve parmi elles. Le détecteur peut être encapsulé dans un objet de façon à ce que la valeur qu'il mesure constitue un attribut. Cette valeur peut alors changer de manière continue, ou à des intervalles réguliers, sans que l'objet ne soit influencé par un autre objet du système.

Exemple :

Vous pouvez modéliser un thermomètre comme un objet ; l'objet a un attribut qui représente la température et qui change de valeur en réponse aux changements de température de l'environnement. Les autres objets peuvent demander la température actuelle en effectuant une opération sur l'objet thermomètre.

Diagramme décrit dans le texte d'accompagnement.

La valeur de l'attribut température change spontanément dans l'objet thermomètre.

Vous pouvez aussi modéliser une valeur encapsulée changeant de cette manière comme un attribut ordinaire, mais vous devriez alors décrire dans la classe de l'objet qu'elle change de manière spontanée.

Visibilité de l'attribut

La visibilité de l'attribut a l'une des valeurs suivantes:

  • Publique: l'attribut est visible aussi bien à l'intérieur qu'à l'extérieur du package contenant la classe.
  • Protégée : l'attribut n'est visible que pour la classe elle-même, ses sous-classes ou des amis de la classe (dépendants du langage)
  • Privée : l'attribut n'est visible que pour la classe elle-même et les amis de la classe
  • Implémentation : l'attribut est visible pour la classe.

La visibilité publique doit être utilisée de façon très rare, seulement lorsqu'un attribut est accessible directement par une autre classe. Définir une visibilité publique est une façon efficace et plus rapide de définir la visibilité de l'attribut comme étant protégée, privée ou implémentation, avec des opérations publiques associées pour obtenir et définir la valeur de l'attribut. La visibilité publique de l'attribut peut être utilisée comme déclaration à un générateur code annonçant que ces opérations d'obtention et de définition doivent être générées automatiquement, faisant gagner du temps pendant la définition de la classe.

La visibilité protégée doit être la visibilité par défaut ; elle protège l'attribut des classes externes, ce qui favorise le couplage lâche et l'encapsulation de comportement.

La visibilité privée doit être utilisée lorsque vous voulez empêcher les sous-classes d'hériter de l'attribut. C'est un moyen de découpler les sous-classes des superclasses et de réduire le besoin de supprimer ou d'exclure les attributs hérités non-utilisés.

La visibilité implémentation est la plus restrictive ; elle ne doit être utilisée que dans les cas où seule la classe est capable d'utiliser l'attribut. C'est une variante de la visibilité privée, qui elle convient dans la plupart des cas.

Structure interne

Certaines classes peuvent représenter des abstractions complexes et avoir une structure complexe. Lors de la modélisation, le concepteur peut souhaiter représenter ses éléments participants internes et leurs relations pour être sûr que l'implémenteur implémentera les collaborations ayant lieu à l'intérieur de la classe en fonction de cela.

Dans UML 2.0, les classes sont définies comme des classes structurées, ayant la capacité d'avoir une structure et des ports internes. Ensuite, les classes peuvent être décomposées en séries de parties connectées qui peuvent à leur tour être décomposées. Une classe peut être encapsulée en forçant des communications externes à passer par des ports obéissants aux interfaces déclarées.

Le concepteur peut donc utiliser des diagrammes de classes pour représenter les relations des classes (comme les associations, les compositions et les agrégations) et les attributs, mais aussi un diagramme de structure composite. Ce diagramme permet au concepteur de montrer la façon dont les instances des parties internes jouent leur rôle à l'intérieur de l'instance d'une classe donnée.

Pour obtenir plus d'informations sur ce sujet, ainsi que des exemples de diagramme de structure composite, voir aussi Concept : classe structurée.