À votre avis, comment est représentée l’information dans Generic System ? Est-ce sous forme de table (de manière scalaire) comme pour les bases de données relationnelles ? Ou plutôt dans un arbre, comme pour les fichiers XML ?
Modèle de représentation de l’information
Generic System enregistre l’information dans un graphe. Celui-ci a une seule racine : le nœud qui représente Engine. Tous les nœuds du graphe sont des objets qui implémentent l’interface Generic, y compris Engine. Ils ont la particularité d’être immuables et ont un cycle de vie simplifié : ils naissent et peuvent mourir. En aucun cas ils ne peuvent être modifiés.
Tous les nœuds contiennent dès leur construction une et une seule donnée. Celle-ci joue un rôle de désignant pour la structure de l’information et un rôle de désigné pour les occurrences de cette information. Par exemple, le nom d’une personne est le désignant et la personne est le désigné. Cette donnée doit être Serializable et implémenter correctement les méthodes equals() et hashCode().
Relations entre les nœuds
Les nœuds sont reliés entre eux par le principe « ancêtres / dépendances » : les ancêtres d’un nœud dépendant doivent exister pendant toute la durée de vie de ce dernier.
Trois types de liaison « ancêtres / dépendances » coexistent dans Generic System : l’instanciation, l’héritage et la composition.
Les ancêtres d’un
Generic sont donc son « meta », ses « supers » et ses « composants ». Ces dépendances sont ses « instances », ses « héritiers » et ses « composites ».
Toutes ces notions peuvent être difficiles à assimiler. Résumons les dans un schéma, afin de bien visualiser l’ensemble :
Sur ce schéma, Generic représente un nœud du graphe. Il comporte les informations suivantes :
- sa value : elle représente la valeur du nœud ;
- son meta : il représente le type du nœud ;
- ses supers : ils représentent les nœuds dont le nœud hérite ;
- ses components : ils représentent les constituants du nœud.
Les Generic du graphe peuvent être reliés de plusieurs façons, représentées sur le schéma ci-dessus par des flèches de différentes couleurs :
- grises : l’instanciation (instances Generic meta);
- blanches : l’héritage (inheritings Generic supers);
- noires : la composition (composites Generic components).
Exemple
Reprenons l’exemple développé dans l’article d’introduction, dans lequel on crée des types, des relations, des attributs ainsi que des instances, links et holders :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// Create a new Engine Engine engine = new Engine(); // Types and sub-types Generic vehicle = engine.addInstance("Vehicle"); Generic color = engine.addInstance("Color"); Generic car = engine.addInstance(vehicle, "Car"); // Attributes and Relations Generic power = vehicle.addAttribute("Power").<span class="crayon-e">enablePropertyConstraint</span><span class="crayon-sy">(</span><span class="crayon-sy">)</span>; Generic vehicleColor = vehicle.addRelation("VehicleColor", color); // Instances Generic myVehicle = vehicle.addInstance("myVehicle"); Generic red = color.addInstance("red"); // Holders and links Generic myPower = myVehicle.addHolder(power, 213); Generic myVehicleRed = myVehicle.addLink(vehicleColor, "myVehicleRed", red); // Persist the changes engine.getCurrentCache().flush(); |
Si nous prenons comme exemple le nœud correspondant au type Vehicle voici les informations que le nœud contient :
- value : “Vehicle” ;
- meta : Engine (type des types) ;
- instances : myVehicle ;
- supers : pas de supers ( Vehicle n’hérite pas d’un autre type) ;
- inheritings : Car ;
- components : pas de components ;
- composites : Power et VehicleColor (attribut et relation qui sont posés sur le type Vehicle).
Détails des liaisons
La liaison d’instanciation
La première liaison est l’instanciation. Cette liaison relie un Generic à son meta ou un Generic à ses instances. Le meta correspond au type de données, les instances correspondent à des occurences de ce type.
Il existe trois niveaux d’instanciation dans Generic System : meta, strucural et concrete.
« meta » est le niveau des meta données. Ce niveau possède plusieurs constituants majeurs :
- Engine : c’est un constituant majeur de ce niveau. C’est le point d’accès du moteur d’information et la racine du graphe. Il joue un rôle particulier, puisque c’est le meta type ou type des types ;
- MetaAttribute est le type des attributs ;
- MetaRelation est le type des relations.
« structural » est le niveau où est représentée la structure de l’information (types, attributs, relations).
Les Generic qu’on y rencontre représentent les instances des meta du niveau meta. Ils s’apparentent à des classes en langage objet ou des tables en base de données relationnelle. Ils portent une information structurelle dont la valeur joue un rôle de désignant.
« concrete » est le niveau où est représentée l’information factuelle (instances, holders, links).
Les Generic de ce niveau portent une information concrète dont la valeur joue un rôle de désigné.
La liaison d’héritage
La deuxième liaison est l’héritage. Cette liaison relie un Generic à ses supers ou un Generic à ses inheritings (héritiers). Les supers correspondent aux types parents (classes mères en langage objet), les inheritings correspondent aux types enfants (classes filles en langage objet).
Un type peut avoir un super (héritage simple) comme plusieurs supers (héritage multiple). Les supers d’un type doivent toujours avoir le même niveau d’instanciation que ce type.
La liaison de composition
La troisième liaison est la composition. Cette liaison relie un Generic à ses components (composants, constituants ou agrégés) ou à ses composites (composés, constitués ou agrégeants). Cette liaison relie par exemple un type à un attribut de ce type.
En fait, c’est une implémentation du patron de conception composite. Il existe trois objets :
- le composant : objet qui peut être spécialisé en une feuille ou un composite ;
- la feuille : objet n’étant pas composé ;
- le composite : objet qui est composé d’un ou plusieurs composants.
La représentation UML du patron de conception composite est la suivante :
Le système de fichiers est un exemple bien connu de la notion de composant/composite :
- le composant : l’objet fichier, qui peut être un fichier ou un répertoire ;
- la feuille : le fichier « normal » (pas un répertoire) ;
- le composite : le répertoire. Celui-ci peut être composé de fichiers et/ou d’autres répertoires (sous-répertoires).
On peut généraliser le principe de composition pour permettre :
- la composition de n types :
Cela permet de former des relations n-aires. Une relation binaire correspond à une relation entre deux types. Une relation unaire correspond à une relation à un seul type : il s’agit en fait d’un attribut. Une relation « vide » correspond à une relation à zéro élément : il s’agit d’un type. - la composition de composition :
Cela permet par exemple de représenter l’attribut d’un attribut.
Grâce à cela, Generic System peut gérer facilement les relations n-aires ainsi que les attributs
Pour reprendre notre exemple de tout à l’heure, voici comment pourraient être représentées les liaisons entre nos différents éléments de notre graphe de Generic :
Comme nous l’avons vu précédemment, Engine est le type des types, MetaAttribute est le type des attributs et MetaRelation le type des relations.
Generic System et UML
Lors de la conception du système de représentation de l’information au sein de Generic System, les choix opérés ont permis de satisfaire les exigences suivantes :
- être recouvrant envers UML ;
- proposer une API intuitive et cohérente ;
- permettre une implémentation robuste.
Chaque diagramme de classe UML peut donc trouver une représentation dans Generic System, bien que l’approche ne soit parfois pas identique.
Dans un diagramme de classe UML, un attribut est « à l’intérieur » de la « boîte » représentant le type. C’est aussi le cas dans les langages objet, puisqu’un attribut est « à l’intérieur » de la classe. Il n’est pas possible d’accéder à l’attribut sans faire référence au type sur lequel il porte :
1 2 3 4 5 6 7 8 9 10 11 12 |
public class Test { // Private attribute private String name; // getter/setter public static void main(String[] args) { // Create an instance of Test Test test = new Test(); // Then we can access the attribute's value test.setName("Hello, world!"); } } |
Dans Generic System, la notion d’attribut est sensiblement différente puisque tout est objet : l’attribut est ainsi représenté sous la forme d’un Generic autonome, qui est lui-même un type pour ses éventuels attributs. L’attribut est alors « à l’extérieur » du type. Il est possible d’ y accéder sans faire référence au type sur lequel il porte.
En revanche, rien ne nous empêche de lier le cycle de vie d’un attribut à celui de son type. Plusieurs contraintes et automatismes sont prévus à cet effet. Par exemple, la suppression d’un type provoquera automatiquement la suppression de l’attribut qui est posé dessus.
Par ailleurs, les valeurs de holders (instances d’attributs) ne sont pas forcément uniques pour une instance donnée, contrairement à UML ou aux langages objet. L’attribut d’un type peut ainsi porter plusieurs valeurs rattachées à une instance de ce type. Par exemple, l’attribut Options possède plusieurs valeurs (“music player” et “air conditioning”). Certains attributs (les propriétés) peuvent néanmoins contraindre l’utilisateur à ne positionner qu’une seule valeur. C’est le cas de la propriété Power de Vehicle.
En résumé
- Generic System représente l’information dans un graphe dont la racine est Engine ;
- Le nœud d’un Generic comporte 4 informations essentielles : sa value, son meta, ses supers et ses components ;
- Les nœuds sont reliés entre eux par le principe « ancêtres / dépendances » : les ancêtres d’un nœud dépendant doivent exister pendant toute la durée de vie de ce dernier. Ce principe se décline en trois types de liaisons.
- l’instanciation qui concerne meta et instances ;
- l’héritage qui concerne supers et inheritings ;
- la composition qui concerne components et composites.