Les instructions suivantes décrivent les étapes impliquées dans la génération de code d'une base de données et dans le
mappage des tables de modèle de
données obtenues aux classes
de conception du modèle
de conception. Ce processus peut être utilisé par le concepteur de base de données afin d'inoculer le développement des
modifications apportées à la base de données dans le cadre d'un cycle de développement d'évolution. Le concepteur de
base de données devra gérer le processus de génération de code tout au long du cycle de développement du
projet. Dans de nombreux cas, le processus de génération de code est effectué dans les premières phases du cycle
de développement du projet et les changements apportés à la conception des données sont gérés de manière incrémentielle
sans qu'il soit nécessaire de procéder à d'autres générations de code de la base de données.
Les grandes étapes du processus de génération de code d'une base de données et de transformation des éléments de modèle
de données obtenus en éléments de modèle de conception sont les suivantes :
-
Créez un modèle physique de données contenant des tables afin de représenter l'agencement physique des données
persistantes dans la base de données. Cette étape peut être réalisée automatiquement à l'aide des outils
fournis avec le système de gestion de base de données relationnelle (SGBDR) ou en recourant à la plupart des outils
modernes de modélisation visuelle.
-
Transformez les tables du modèle physique de données en classes de conception dans le modèle de
conception. Cette étape peut être réalisée à l'aide d'une combinaison d'outils automatisés utilisés pour la
transformation initiale, suivie de réglages manuels.
-
Définissez des associations entre les classes du modèle de conception.
-
Définissez les opérations appropriées sur les classes du modèle de conception à partir des actions effectuées sur
les éléments de modèle de données correspondants.
-
Regroupez les classes du modèle de conception en sous-systèmes et packages, selon les besoins.
Le processus de génération de code d'une base de données ou d'un script de langage de définition de données produit
généralement un ensemble d'éléments de modèle (tables, vues, procédures stockées, etc.). Selon la complexité de la base
de données, le concepteur de base de données peut être contraint de partitionner les éléments de modèle soumis à la
génération de code en packages de domaine qui contiennent des ensembles de tables logiquement liés.
La procédure suivante peut être observée pour produire des classes de conception à partir d'éléments de modèle du
modèle de données. Répliquer la structure de la base de données dans un modèle de classe est relativement simple. Le
processus détaillé ci-dessous décrit l'algorithme de transformation des éléments de modèle de données en éléments de
modèle de conception.
Le tableau ci-dessous contient un résumé du mappage général entre les éléments de modèle de conception et les éléments
de modèle de données.
Elément de modèle de données
|
Elément de modèle de conception correspondant
|
Table
|
Classe
|
Colonne
|
Attribut
|
Relation non identifiante
|
Association
|
Table d'intersection
|
Classe d'association
Association plusieurs à plusieurs
Association qualifiée
|
Relation identifiante
|
Agrégation
|
Cardinalité
|
Multiplicité
|
Contrainte de vérification de table avec une clause d'énumération
|
Classe <<ENUM>>
|
Schéma
|
Package
|
Certains éléments de modèle du modèle de données n'ont aucune corrélation directe dans le modèle de conception. Ces
éléments comprennent les espaces de table et la base de données elle-même, qui modèlent les caractéristiques de
stockage physique de la base de données et sont représentés comme des composants. On trouve également les vues de la
base de données, qui constituent des tables « virtuelles » et n'ont aucun sens dans le modèle de conception. Pour
finir, les index des clés primaires des tables et les fonctions de déclenchement de la base de données, qui sont
utilisés pour optimiser le fonctionnement de la base de données, n'ont de sens que dans le contexte de la base de
données et du modèle de données.
Pour chaque table que vous souhaitez transformer, créez une classe pour représenter cette table. Pour chaque colonne,
créez un attribut sur la classe avec le type de données qui convient. Essayez de faire correspondre du mieux possible
le type de données de l'attribut et celui de la colonne associée.
Exemple
Etudiez la table de base de données Client, avec la structure suivante, représentée dans la figure ci-dessous :
Nom de colonne
|
Type de données
|
ID_client
|
Numéro
|
Nom
|
Varchar
|
Rue
|
Varchar
|
Ville
|
Varchar
|
Etat/Province
|
Char(2)
|
Code postal
|
Varchar
|
Pays
|
Varchar
|
Définition de table pour la table Client
A partir de ce point, nous créons une classe, Client, dotée de la structure indiquée dans la figure suivante :
Classe Client initiale
Dans cette classe Client initiale, on compte un attribut pour chaque colonne de la table Client. Chaque
attribut se caractérise par une visibilité publique, car n'importe laquelle des colonnes de la table d'origine
peut faire l'objet d'une requête.
Nous attirons votre attention sur l'icône « + » affichée à gauche de l'attribut, et qui indique que ce dernier est «
public » ; par défaut, tous les attributs dérivés des tables SGBDR doivent être publics, car les SGBDR autorisent
généralement que n'importe quelle colonne fasse l'objet de requêtes sans aucune restriction.
La classe qui découle du mappage direct table-classe contient souvent des attributs pouvant être séparés en une classe
distincte, particulièrement dans les cas où ces attributs apparaissent dans un certain nombre de classes traduites. Ces
« attributs redondants » peuvent découler d'une dénormalisation des tables (opérée pour des raisons de performances) ou
peuvent être la conséquence d'un modèle de données trop simplifié. Dans ces cas de figure, divisez la classe
correspondante en deux classes ou plus afin de représenter une vue normalisée des tables.
Exemple
Après avoir défini la classe Client ci-dessus, nous pouvons définir une classe Adresse qui contient
toutes les informations liées à l'adresse (en présumant que notre système comportera d'autres parties dotées
d'adresses), ce qui nous donne les classes suivantes :
Classe Client revue, avec la classe Adresse extraite
L'association établie entre ces deux classes est une agrégation, car l'adresse du client peut être envisagée
comme faisant partie du client.
Pour chaque relation de clé externe de la table, créez une association entre les classes associées, en supprimant
l'attribut de la classe qui mappait à la colonne de clé externe. Si cette colonne était initialement représentée comme
un attribut, supprimez-le de la classe.
Exemple
Prenons la structure de la table Commande (order) détaillée ci-dessous :
Nom de colonne
|
Type de données
|
Numéro
|
Numéro
|
ID_client
|
Varchar
|
Structure de la table Commande
Dans la table Commande ci-dessus, la colonne ID_client est une référence de clé externe ; cette
colonne contient la valeur de clé primaire du Client associé à la Commande. Nous représenterions cela dans le modèle de
conception comme suit :
Représentation des relations de clé externe dans le modèle de conception
La clé externe est représentée comme une association entre les classes Commande et Article.
Les modèles de données SGBDR représentent des relations plusieurs à plusieurs avec ce qu'on appelle une table de
jointure ou table d'association. Ces tables permettent aux relations plusieurs à plusieurs d'être
représentées à l'aide d'une table intermédiaire qui contient les clés primaires de deux tables différentes pouvant être
associées. La raison pour laquelle les tables de jointure sont nécessaires réside dans le fait qu'une référence de clé
externe ne peut contenir de référence qu'à une seule valeur de clé externe ; lorsqu'une ligne unique est susceptible
d'être liée à de nombreuses autres lignes d'une autre table, une table de jointure est nécessaire afin de les associer.
Exemple
Prenons le cas de Produits, qui peuvent être fournis par n'importe quel fournisseur d'un groupe de
Fournisseurs, sachant que n'importe quel Fournisseur peut fournir autant de Produits qu'il le
souhaite. Les tables Produit et Fournisseur ont la structure définie ci-dessous :
Table de produits
Nom de colonne
|
Type de données
|
ID_produit
|
Numéro
|
Nom
|
Varchar
|
Description
|
Varchar
|
Prix
|
Numéro
|
|
Table de fournisseur
Nom de colonne
|
Type de données
|
ID_fournisseur
|
Numéro
|
Nom
|
Varchar
|
Rue
|
Varchar
|
Ville
|
Varchar
|
Etat/Province
|
Char(2)
|
Code postal
|
Varchar
|
Pays
|
Varchar
|
|
Définitions de tables Produit et Fournisseur
Pour relier ces deux tables entre elles afin de déterminer quels sont les produits proposés par un fournisseur donné,
nous avons besoin d'une table Produit-Fournisseur, qui est définie dans la table ci-dessous.
Table Produit-Fournisseur
|
Nom de colonne
|
Type de données
|
ID_produit
|
Numéro
|
ID_fournisseur
|
Numéro
|
Définition de la table Produit-Fournisseur
Cette table de jointure contient les clés primaires des produits et des fournisseurs, en les reliant. La
présence d'une ligne dans la table indiquerait qu'un fournisseur donné propose un produit donné. Toutes les lignes dont
la colonne ID_fournisseur correspond à un identifiant de fournisseur donné afficheraient la liste de tous les produits
proposés par ce fournisseur.
Dans le modèle de conception, cette table intermédiaire est redondante, car un objet de modèle peut représenter
directement les associations plusieurs à plusieurs. Les classes Fournisseur et Produit et leurs relations
sont représentées dans la figure ci-dessous, ainsi que la classe Adresse, qui est extraite de
Fournisseur, comme nous l'avons vu.
Représentation des classes Produit et Fournisseur
Vous rencontrerez souvent des tables dont les structures sont assez similaires. Dans le modèle de données, il n'existe pas de concept de généralisation et il
n'y a donc aucun moyen de représenter le fait que deux tables ou plus ont une structure en commun. Les structures
communes résultent parfois de la dénormalisation opérée pour des questions de performances, comme c'était le cas pour
la table « implicite » Adresse évoquée plus haut, que nous avons extraite en une classe distincte. Dans d'autres
cas, les tables partagent plus de caractéristiques fondamentales, que nous pouvons extraire dans une classe parente
généralisée avec deux sous-classes ou plus. Pour identifier les possibilités de généralisation, recherchez les colonnes
redondantes dans plusieurs tables, là où les tables présentent plus de similitudes que de différences.
Exemple
Etudiez les tables suivantes, SoftwareProduct et HardwareProduct, représentées ci-dessous :
Table Software Product
Nom de colonne
|
Type de données
|
ID_produit
|
Numéro
|
Nom
|
Varchar
|
Description
|
Varchar
|
Prix
|
Numéro
|
Version
|
Numéro
|
|
Table Hardware Product
Nom de colonne
|
Type de données
|
ID_produit
|
Numéro
|
Nom
|
Varchar
|
Description
|
Varchar
|
Prix
|
Numéro
|
Version
|
Numéro
|
|
Tables SoftwareProduct et HardwareProduct
Remarquez que les colonnes surlignées en bleu sont identiques ; ces deux tables partagent une majeure partie de leur
définition et ne diffèrent que légèrement. Nous pouvons représenter ce phénomène en procédant à l'extraction d'une
classe Produit commune, avec SoftwareProduct et HardwareProduct comme sous-classes de
Produit, comme indiqué dans la figure suivante :
Classes SoftwareProduct et HardwareProduct, montrant la généralisation à la classe Produit
En regroupant l'ensemble des définitions de classes, la figure ci-dessous montre un diagramme de classes consolidé pour
le système Saisie de commande (classes principales uniquement).
Diagramme de classes consolidé pour le système Saisie de commande
Répliquer le comportement est plus difficile, car les bases de données relationnelles ne sont généralement pas
orientées objet et ne semblent rien avoir de commun avec les opérations réalisées sur une classe du modèle d'objet. Les
étapes suivantes peuvent aider à recréer le comportement des classes identifiées ci-dessus :
-
Créez des opérations afin d'obtenir et de régler chaque attribut. Il doit exister un moyen de régler, de
changer et d'effectuer des requêtes sur les valeurs des attributs d'objets. Etant donné que l'unique façon
d'accéder aux attributs d'un objet est de le faire via les opérations fournies par la classe, ces opérations
doivent être définies sur la classe. Lors de la création des opérations qui définissent la valeur d'un attribut,
veillez à bien incorporer toute contrainte de validation susceptible d'être active sur la colonne associée. En
l'absence de contraintes de validation, on peut choisir de représenter simplement le fait que les attributs peuvent
être obtenus et réglés en les marquant comme ayant une visibilité « publique », comme cela a déjà été
fait dans les diagrammes ci-dessus(avec l'icône située à gauche du nom de l'attribut).
-
Créez une opération sur la classe pour chaque procédure stockée qui fonctionne sur la table associée. Les
procédures stockées sont des sous-routines exécutables qui s'exécutent au sein même du SGBD. Cette logique doit
être traduite dans le modèle de conception. Si une procédure stockée ne fonctionne que sur une classe, créez une
opération sur cette classe avec les mêmes paramètres et le même type de retour que la procédure stockée. Documentez
le comportement de la procédure stockée dans l'opération, en veillant à noter dans la description de la
méthode que l'opération est implémentée par la procédure stockée.
-
Créez des opérations afin de gérer les associations entre les classes. Lorsqu'il existe une association
entre deux classes, il doit exister une manière de créer, de gérer et de supprimer les associations. Les
associations entre objets sont gérées via les références d'objets, ce qui signifie que pour créer une association
entre une Commande et un LineItem (c'est-à-dire pour ajouter le LineItem à la
Commande), une opération sur Commande doit être appelée, transformant le LineItem en argument
(Order.add(aLineItem)). Il doit également exister des façons de supprimer et de mettre à jour l'association
(c'est-à-dire Order.remove(aLineItem) et Order.change(aLineItem,aNewLineItem)).
-
Gestion de la suppression d'objet. Si la langue cible prend en charge la suppression explicite, ajoutez le
comportement au destructeur de la classe qui implémente la vérification de l'intégrité référentielle. Dans les cas
où il existe des contraintes d'intégrité référentielle dans la base de données, telles que suppression en
cascade, le comportement doit être répliqué dans les classes appropriées. Par exemple, la base de données peut
définir une contrainte prévoyant que chaque fois qu'une Commande est supprimée, tous les LineItems
associés doivent l'être également. Si la langue cible prend en charge la récupération de place, créez un mécanisme
permettant de supprimer les lignes des tables lorsque l'objet associé est soumis à une récupération de place. Il
est à noter que cette action est plus difficile qu'il n'y paraît (bien qu'elle paraisse déjà difficile), car vous
devrez implémenter un mécanisme permettant de garantir qu'aucun client de base de données n'a aucune référence à
l'objet devant être soumis à une récupération de place ; il ne suffit pas de se fier aux capacités de récupération
de place de l'environnement d'exécution/machine virtuelle car il s'agit uniquement de la vue qu'un client a du
monde.
-
Gestion du comportement sous-entendu par les requêtes. Examinez les instructions SELECT qui accèdent à la
table pour voir comment les informations sont récupérées et manipulées. Pour chaque colonne renvoyée directement
par une instruction SELECT, réglez la propriété publique de l'attribut associé sur vrai ; tous les
autres attributs doivent être réglés sur privé. Pour chaque colonne calculée dans une instruction SELECT,
créez une opération sur la classe associée pour calculer et renvoyer la valeur. Lors de la sélection des
instructions SELECT, incluez également les instructions SELECT intégrées dans les définitions de vue.
Les classes de conception créées à partir des transformations de table en classes doivent être organisées dans des packages de conception appropriés et/ou sous-systèmes de conception du modèle de conception, selon les
besoins, en fonction de la structure architecturale globale de l'application. Pour un aperçu de l'architecture de
l'application, consultez Concept : Structures en
couches et Concept :
Architecture logicielle.
|