Concepts : Mécanismes de conception et d'implémentation
Rubriques
Un mécanisme de conception
est le perfectionnement du mécanisme d'analyse correspondant (voir également Concepts : 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 pour les mécanismes d'analyse, un mécanisme de conception peut instancier un ou plusieurs patterns, en l'occurrence des schémas architecturaux
ou 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 idiomes
ou modèles 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'à un total d'1 Mb (taille x volume) ; accès très rapide en lecture, écriture et mise à jour.
- Carte Flash ; caractéristiques : jusqu'à 8 Mb ; accès lent en mise à jour et en écriture ; moyen en lecture.
- Fichier binaire ; caractéristiques : entre 100 Kb et 200 Mb ; 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 Kb (il n'y a pratiquement pas de limite supérieure) ; accès encore plus lent en lecture, écriture et mise à jour.
Notez que ces vitesses ne sont jugées "lentes" que par rapport au stockage en mémoire. Bien évidemment, dans certains environnements, l'utilisation de mémoire cache peut améliorer les temps d'accès apparents.

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é.
Procédez de façon itérative pour perfectionner le mappage entre mécanismes de conception et d'implémentation, en éliminant les chemins redondants et en travaillant à la fois "de façon descendante" et
"ascendante".
Descendante. En travaillant "de façon descendante", les nouvelles réalisations de cas d'utilisation et les réalisations perfectionnées imposeront de nouvelles exigences aux mécanismes de conception requis par l'intermédiaire des 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.
Ascendante. En travaillant "de façon ascendante",
et en examinant les mécanismes d'implémentation disponibles, vous pourriez trouver des produits
qui correspondent à plusieurs mécanismes de conception à la fois, mais requièrent une certaine adaptation ou une redistribution de 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 constituer 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 plus d'un mécanisme de conception soit associé à 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 standards (systèmes d'exploitation et middleware), il convient de réaliser une certaine optimisation que l'on basera sur le coût, la désadaptation 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 à des mécanismes d'implémentation comme suit :

Un mappage possible entre mécanismes d'analyse et mécanismes de conception. Les flèches en pointillés signifient "est spécialisé par", ce qui implique que les caractéristiques des mécanismes de conception sont héritées des mécanismes d'analyse mais qu'elles 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 des indications concernant leur utilisation, sont documentés dans Artefact :
Principes et conseils relatifs à un projet. La relation (ou mappage) des mécanismes d'analyse aux mécanismes de conception et d'implémentation, de même que le raisonnement associé menant à ces choix, sont documentés dans Artefact : Document d'architecture logicielle.
A l'instar des mécanismes d'analyse, les mécanismes de conception peuvent être modélisés au moyen d'une collaboration,
qui peut instancier un ou plusieurs schémas architecturaux
ou conceptuels.
Exemple : Un mécanisme de persistance
Cet exemple s'appuie sur une instance d'une persistance basée sur un SGBDR et tirée de JDBC
(Java Data Base Connectivity). Bien que nous en présentions ici la conception,
JDBC fournit le véritable 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.
Le modèle vue statique : JDBC présente les classes (les rôles classifieurs, exclusivement) de la collaboration.
Vue statique : JDBC
Les classes en jaune sont celles qui ont été fournies, les autres (myDBClass, etc.) ont été dévolues 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. Statement est ce 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 aplanit 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.
La liste de classes persistantes (PersistentClassList) sert à renvoyer un ensemble d'objets persistants comme résultat d'une requête de base de données (par ex., 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. DriverManagertente 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 cbdj:sous-protocole:sous-nom. Cette URL sert à localiser le serveur réel de la base de données et n'est, dans cet exemple, pas reliée à la Web.
utilisateur : L'utilisateur de la base de données au nom duquel la connexion est établie
mot de passe : Le 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 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 rapatrier 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 : Mise à jour
Pour mettre à jour une classe, le client de persistance adresse sa demande à la classe DBClass. Celle-ci rapatrie les données depuis l'objet PersistentClass donné, puis crée une nouvelle instruction au moyen de l'opération createStatement() de la classe de Connection. Une fois l'instruction mise au point, la mise à jour est exécutée et la base de données mise à jour avec les nouvelles données de la classe.
Souvenez-vous : c'est le rôle de la classe DBClass d'"aplanir" la classe PersistentClass et de l'écrire dans la base de données. C'est pourquoi elle doit être rapatriée 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. DBClass crée alors un 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 seront prises à propos du mappage de la classe DBClass aux classes persistantes, par exemple d'avoir une classe DBClass par classe persistante et de l'attribuer aux packages appropriés.
Ces packages seront dépendants du package java.sql fourni (voir la documentation de l'API JDBC) qui contient les classes de support DriverManager, Connection, Statement et ResultSet.
|