Un mécanisme de conception est le perfectionnement du mécanisme d'analyse correspondant (voir aussi Concept :
Mécanismes d'analyse). Un mécanisme de conception ajoute des détails concrets au mécanisme d'analyse conceptuelle,
tout en ne nécessitant pas de technologie particulière - par exemple l'implémentation par un fournisseur particulier
d'un système de gestion de base de données orientée objet. Comme les mécanismes d'analyse, un mécanisme de conception
peut instancier un ou plusieurs patterns, en l'occurrence des patterns architecturaux ou des patterns de
conception.
De la même façon, un mécanisme d'implémentation est le perfectionnement du mécanisme de conception
correspondant, utilisant, par exemple, un langage de programmation spécifique ainsi qu'une autre technologie
d'implémentation (comme le produit middleware d'un fournisseur spécifique.) Un mécanisme d'implémentation peut
instancier un ou plusieurs schémas de mise en
oeuvre ou patterns d'implémentation.
Observons les mécanismes d'analyse de la persistance :
-
Il peut être nécessaire de stocker de nombreux (2000) petits objets (200 octets chacun) pendant quelques secondes,
sans besoin de survie.
-
Il peut être nécessaire de stocker plusieurs très gros objets sur disque en permanence pendant plusieurs mois, sans
les mettre à jour, mais avec des moyens d'extraction sophistiqués.
Ces objets nécessiteront plusieurs supports de persistance ; on peut identifier
les caractéristiques suivantes en ce qui concerne les mécanismes de conception pour la prise en charge de la
persistance :
-
Stockage en mémoire ; caractéristiques : jusqu'à 1 Mo (taille x volume) ; accès très rapide en lecture, en
écriture et en mise à jour.
-
Carte Flash ; caractéristiques : jusqu'à 8 Mo ; accès lent en mise à jour et en écriture ; moyen en lecture.
-
Fichier binaire ; caractéristiques : entre 100 Ko et 200 Mo ; mise à jour lente ; accès lent en lecture et
en écriture.
-
Système de gestion de base de données (SGBD) ; caractéristiques : à partir de 100 Ko (il n'y a pratiquement
pas de limite supérieure) ; accès encore plus lent en lecture, en écriture et en mise à jour.
Notez que ces vitesses ne sont jugées "lentes" que par rapport au stockage en mémoire. Visiblement, l'utilisation
du stockage en mémoire cache peut améliorer les temps d'accès apparents dans certains environnements.
Initialement, le mappage entre mécanismes de conception et d'implémentation ne sera probablement pas optimal mais il
permettra au projet d'avancer, identifiera les risques pas encore décelés et entraînera des enquêtes et des évaluations
supplémentaires. A mesure que le projet se poursuit et que le niveau de connaissance augmente, le mappage doit être
perfectionné.
Il faut procéder de manière itérative pour perfectionner le mappage entre les mécanismes de conception et
d'implémentation en éliminant les chemins redondants et en travaillant de manière "descendante" et "ascendante".
Travail descendant. Lorsque vous descendez, de nouvelles réalisations de cas d'utilisation perfectionnées vont
dégager de nouvelles exigences dans les mécanismes de conception requis via les mécanismes d'analyse requis. Ces
nouvelles exigences peuvent dégager certaines caractéristiques supplémentaires d'un mécanisme de conception, obligeant
à séparer les mécanismes. Il y a également un compromis entre la complexité du système et ses performances :
-
Une trop grande variété de mécanismes de conception rend le système trop complexe.
-
Une trop faible diversité de ces mécanismes peut créer, pour certains mécanismes d'implémentation, des problèmes de
performances qui repoussent les limites des plages raisonnables de leurs valeurs caractéristiques.
Travail ascendant. Lorsque vous remontez et examinez les mécanismes d'implémentation disponibles, vous pouvez
trouver des produits répondant à plusieurs mécanismes de conception à la fois mais nécessitent d'adapter ou de séparer
vos mécanismes de conception. Vous voulez minimiser le nombre de mécanismes d'implémentation que vous utilisez, mais
une absence de diversité peut également poser des questions de performances.
Si vous décidez d'utiliser un SGBD pour stocker des objets de classe A, vous pourriez être tenté d'y recourir pour
stocker tous les objets du système. Cela peut s'avérer particulièrement inefficace, ou encombrant. Tous les objets qui
nécessitent de la persistance ne doivent pas forcément être stockés dans le SGBD. Certains objets peuvent être
persistants mais peuvent être régulièrement appelés par l'application, et seulement rarement par d'autres applications.
La meilleure approche peut consister en une stratégie hybride dans laquelle l'objet est lu à partir du SGBD vers la
mémoire et périodiquement synchronisé.
Exemple
Un vol peut être stocké en mémoire pour un accès rapide, et dans un SGBD pour une persistance à long terme ; cela
déclenche toutefois le besoin d'un mécanisme qui synchronise les deux.
Il n'est pas rare que plusieurs mécanismes de conception soient associés à une classe client comme compromis entre
différentes caractéristiques.
Etant donné que les mécanismes d'implémentation prennent souvent la forme de groupes de composants standard (systèmes
d'exploitation et middleware), il convient de réaliser une certaine optimisation que l'on basera sur le coût, le défaut
d'adaptation d'impédance ou l'uniformité du style. Par ailleurs, les mécanismes sont souvent interdépendants, rendant
difficile la séparation claire des services en mécanismes de conception.
Exemples
Le perfectionnement se poursuit tout au long de la phase d'élaboration, et se présente toujours comme un compromis
entre :
-
Une "correspondance" exacte du mécanisme de conception avec les exigences du client, en termes de caractéristiques
attendues.
-
Le coût et la complexité que suppose le fait d'avoir trop de mécanismes d'implémentation différents à acquérir et à
intégrer.
L'objectif général est toujours d'avoir un seul ensemble clair de mécanismes afin de donner à un système de grande
taille une intégrité conceptuelle, de la simplicité et de l'élégance.
Les mécanismes de conception de persistance peuvent être mappés aux mécanismes d'implémentation de la manière
suivante :
Un mappage possible entre mécanismes d'analyse et mécanismes de conception. Les flèches en pointillés signifient "est
spécialisé par", sous-entendu que les mécanismes de conception tiennent leurs caractéristiques des mécanismes d'analyse
mais que celles-ci seront spécialisées et perfectionnées.
Une fois que vous avez terminé l'optimisation des mécanismes, les mappages suivants sont possibles :
Les décisions de conception pour une classe client en termes de mappage entre mécanismes ; la classe de vol
nécessite deux types de persistance : un stockage en mémoire implémenté par un sous-programme de bibliothèque prête
à l'emploi, et un autre dans une base de données implémentée à l'aide d'un produit StockageObjet standard.
On doit pouvoir naviguer sur l'application dans les deux directions, de manière à ce qu'il soit facile de déterminer
les classes client lors des changements de mécanismes d'implémentation.
Les mécanismes de conception, ainsi que les détails relatifs à leur utilisation, sont documentés dans le Produit : Instructions relatives au projet. La relation (ou le
mappage) des mécanismes d'analyse aux mécanismes de conception et d'implémentation, avec le raisonnement associé à ces
choix, est documentée dans le Produit: Document d'architecture logicielle.
Comme pour les mécanismes d'analyse, les mécanismes de conception peuvent être modélisés à l'aide d'une collaboration
qui peut instancier un ou plusieurs patterns d'architecture ou de conception.
Exemple : Un mécanisme de persistance
Cet exemple s'appuie sur une instance d'un pattern pour la persistance SGBDR, tiré de JDBC (Java Data Base Connectivity). Bien
que nous présentions ici la conception, JDBC fournit du code pour certaines de ses classes ; il n'y a donc qu'un pas
entre ce qui est présenté ici et un mécanisme d'implémentation.
La figure Vue statique : JDBC présente les classes (les rôles discriminants) de la collaboration.
Vue statique : JDBC
Les classes en jaune sont celles qui ont été fournies, les autres (myDBClass, etc.) ont été ajoutées par le concepteur
pour créer le mécanisme.
Dans JDBC, un client travaillera avec une classe de base de données (DBClass) pour lire et écrire les données
persistantes. DBClass est chargée d'accéder à la base de données JDBC en utilisant la classe DriverManager. Dès
qu'une connexion à la base de données est ouverte, DBClass peut créer des instructions SQL qui seront envoyées
au SGBDR sous-jacent et exécutées au moyen de la classe Statement. La classe Statement représente l'élément qui
"parle" à la base de données. Le résultat de la requête SQL est renvoyé dans un objet ResultSet.
La classe DBClass est chargée de rendre persistante une autre instance de classe. Elle comprend le mappage
OO-à-SGBDR et elle a le comportement idoine pour servir d'interface avec le SGBDR. La classe DBClass décompose l'objet,
l'écrit dans le SGBDR et lit ses données depuis le SGBDR, puis elle le
construit. Chaque classe persistante aura une DBClass correspondante.
PersistentClassList sert à renvoyer un ensemble d'objets persistants comme résultat d'une requête de base de
données (par exemple, DBClass.read()).
Nous présentons maintenant une série de vues dynamiques, afin de montrer comment le mécanisme fonctionne réellement.
JDBC : Initialiser
L'initialisation doit avoir lieu avant qu'il ne soit possible d'accéder à toute classe persistante.
Pour initialiser la connexion à la base de données, la classe DBClass doit charger le pilote approprié en appelant
l'opération DriverManager getConnection() avec une URL, un utilisateur et un mot de passe.
L'opération getConnection() tente d'établir une connexion à l'URL de la base de données en question. DriverManager
tente de sélectionner un pilote approprié dans l'ensemble de pilotes JDBC répertoriés.
Paramètres :
url : Une URL de base de données du type jdbc:sous-protocole:sous-nom. Cette URL sert à localiser le serveur de
base de données et n'est, dans cet exemple, pas reliée au Web.
utilisateur : utilisateur de la base de données au nom duquel la connexion est établie
mot de passe : mot de passe de l'utilisateur
Renvoie :
une connexion à l'URL.
JDBC : Créer
Pour créer une nouvelle classe, le client de persistance adresse sa demande à DBClass. Celle-ci crée une nouvelle
instance de PersistentClass avec des valeurs par défaut. Puis elle crée une nouvelle instruction au moyen de
l'opération createStatement() de la classe de connexion. L'instruction est exécutée et les données sont insérées dans
la base de données.
JDBC : Lire
Pour lire une classe persistante, le client de persistance adresse sa demande à la classe DBClass. Celle-ci crée alors
une nouvelle instruction au moyen de l'opération createStatement() de la classe Connection. L'instruction est exécutée
et les données sont renvoyées dans un objet ResultSet. DBClass crée alors une nouvelle instance de la classe
PersistentClass et la remplit avec les données récupérées. Les données sont renvoyées dans un objet de type Collection,
qui est une instance de la classe PersistentClassList.
Remarque : La chaîne transmise à executeQuery() n'est pas nécessairement exactement la même chaîne que celle qui a été
transmise par read(). DBClass construira la requête SQL pour extraire les données persistantes de la base de données,
en utilisant les critères transmis à read(). Cela s'explique par le fait que nous ne voulons pas que le client de
DBClass ait besoin de connaître la structure interne de la base de données pour créer une requête valide. Cette
connaissance est encapsulée dans la classe DBClass.
JDBC : Mettre à jour
Pour mettre à jour une classe, le client de persistance adresse sa demande à la classe DBClass. Celle-ci extrait les
données depuis l'objet PersistentClass donné, puis crée une nouvelle instruction au moyen de l'opération
createStatement() de la classe Connection. Une fois l'instruction créée, la mise à jour est exécutée et la base de
données est mise à jour avec les nouvelles données de la classe.
Ne pas oublier : C'est la classe DBClass qui "regroupe" l'objet PersistentClass et l'écrit dans la base de données.
C'est pourquoi elle doit être extraite de la classe PersistentClass en question avant que ne soit créée l'instruction
SQL.
Remarque : Dans le mécanisme ci-dessus, PersistentClass doit fournir des sous-programmes d'accès à toutes les données
persistantes afin que la classe DBClass puisse y accéder. Cela permet d'avoir un accès externe à certains attributs
persistants qui, autrement, auraient été d'accès privé. C'est le prix à payer pour extraire les informations de
persistance de la classe où sont encapsulées les données.
JDBC : Supprimer
Pour supprimer une classe, le client de persistance demande à la classe DBClass de supprimer la classe PersistentClass.
Celle-ci crée alors une nouvelle instruction au moyen de l'opération createStatement() de la classe Connection.
L'instruction est exécutée et les données sont retirées de la base de données.
Dans l'implémentation de cette conception, certaines décisions doivent être prises à propos du mappage de la classe
DBClass aux classes persistantes, par exemple avoir une classe DBClass par classe persistante et l'attribuer aux
packages appropriés. Ces packages seront dépendants du package java.sql fourni (voir JDBC™
Documentation API) qui contient les classes de support DriverManager, Connection, Statement et ResultSet.
|