La «dérivation» des composants

On ne prend conscience de l'intérêt et de la puissance du concept « Composant » que lorsqu'on a compris les mécanismes de dérivation. En effet, l'innovation la plus surprenante qu'apporte Oxygène++ réside dans la possibilité de spécialiser des  composants standards pour les adapter aux besoins spécifiques des utilisateurs.

C'est en s'appuyant sur des composants standards que le développeur sera le plus efficace, puisqu'il bénéficie d'ores et déjà de fonctionnalités évoluées. Il n'a plus qu'à se concentrer sur les adaptations demandées par son client.

 

Le concept « composant » va d'ailleurs au delà des problèmes de développement.

  • Il profite également aux commerciaux qui en présentant les composants de base à un prospect sont en mesure de convaincre avant même de connaître les problèmes spécifiques du client.

  • Il facilite la réalisation du cahier des charges, puisque la réflexion autour d'un logiciel déjà construit simplifie l'identification des besoins spécifiques et évite la réalisation d'une maquette.

Les différents niveaux d'intervention

Pour adapter son logiciel, le développeur est en mesure d'intervenir à différent niveau :

  • on peut enrichir les données privées du composant en ajoutant des champs dans les tables,

  • on peut enrichir les données publiques du composant en modifiant la structure d'interface,

  • on peut modifier les écrans et impressions pour faire apparaître les nouvelles données.

  • on peut définir ou redéfinir les méthodes publiques de l'objet l'interface du composant,

  • on peut dériver les objets du composant de base pour ajouter des contrôles,

  • on peut remplacer un objet du composant de base par un objet plus optimal,

  • on peut ajouter de nouveaux objets qui prendront en charge une nouvelle fonctionnalité,

  • on peut mettre un composant de base en relation avec un nouveau composant.

 

Grâce aux automatismes d'Oxygène++, les cas simples ne nécessitent aucune programmation car les mises à jour des écrans et de la base de données sont prises en compte dynamiquement (pas de traduction, compilation, édition de liens).

Exemple de dérivation

Pour illustrer nos exemples nous allons d'abord décrire le composant de base que nous dériverons par la suite.

 

Source du composant de base :

Composant PERSONNE "Les personnes"

Declaration
   BASE : "DOC"
    GLOBAL : "PERSONNE"
FinDeclaration

Objet INTERFACE : INTERFACE "Interface du composant personne"
    Methode "IMPRIME"
        CreerObjet "PERSONNE"."IMPRIME_LISTE"
    FinMethode
FinObjet

Objet GESTION : GESTION "Gestion des personnes"
    Methode "IMPRIME"
        AppliquerMethodeComposant "PERSONNE"."IMPRIME"
    FinMethode
FinObjet

Objet EDITION_ETAT : IMPRIME_LISTE "Impression de la liste des personnes"
    Declaration
        TABLE : "PERSONNE"
        ETAT : "LISTE"
     FinDeclaration
FinObjet

Explications

Le composant PERSONNE est constitué des trois objets suivants :

  • L'objet INTERFACE qui est obligatoire,

  • L'objet GESTION conçu pour gérer la table des personnes

  • L'objet IMPRIME_LISTE qui imprime la liste des personnes

L'objet INTERFACE contient une méthode publique IMPRIME qui fait appel à l'objet IMPRIME_LISTE. L'objet GESTION contient également une méthode IMPRIME qui lance l'objet d'impression en invoquant la méthode publique IMPRIME de son propre composant.

Etape 1 : Amélioration de l'interface utilisateur

L'amélioration de l'interface utilisateur ne nécessite pas forcément de programmation. On peut très facilement améliorer l'esthétique d'un composant de base en modifiant ses écrans et ses états depuis les éditeurs d'Oxygène++. Il suffit ensuite de refaire le global de l'application.

Bien évidemment la plupart du temps, on souhaite améliorer l'interface en modifiant les écrans, mais sans écraser les écrans standards de l'application. Dans ce cas, il faut impérativement stocker les nouveaux écrans dans un nouveau global qui sera associé au composant dérivé. Pour cela une programmation minimale est nécessaire.

 

Composant PERSONN2 "Le composant PERSONNE enrichi"
Declaration
   BASE : "DOC"
   GLOBAL : "PERSONN2","PERSONNE"
FinDeclaration

Objet PERSONNE.INTERFACE : INTERFACE "Interface enrichie du composant personne"
FinObjet

Explications

Dans le source ci-dessus, nous avons dérivé l'objet INTERFACE du composant PERSONNE. Notez que pour le moment l'objet INTERFACE ne contient pas de programmation spécifique. Seule la déclaration GLOBAL a été ajoutée dans la section des déclarations globales du composant. Notez également que la déclaration est valable pour tous les objets de composant de base.

En d'autres termes, les objets GESTION et IMPRIME_LISTE du composant PERSONNE rechercheront désormais leurs ressources (écrans, états, bitmaps, etc) en priorité dans le global PERSONN2.GLB puis dans PERSONNE.GLB.

 

La dérivation de l'objet INTERFACE n'est pas anodine puisqu'elle établit une dépendance entre la DLL (Dynamic Link Library) du composant PERSONNE et la DLL du composant PERSONN2. Concrètement, le chargement de la DLL du composant dérivé entraînera le chargement de la DLL du composant de base. Ce mécanisme est standard en C++, il est déterminé au moment de l'édition de liens (Link) de l'application. 

La syntaxe de la déclaration GLOBAL a évolué pour permettre la dérivation de composant.

Etape 2 : Ajout d'un champ dans la base de données

Ici aussi, la programmation n'est pas forcément nécessaire, l'ajout de champs dans la table principale de notre composant s'effectue depuis le descripteur de données d'Oxygène++.

Tant que les nouveaux champs n'interviennent pas dans des calculs complexes et ne nécessitent pas de contrôles spéciaux, il suffit de les faire apparaître dans les écrans et dans les états du composant (comme décrit à l'étape 1) pour qu'il soient exploités par le logiciel. La recompilation des sources n'est donc pas nécessaire.

 

L'ajout d'une relation vers un autre composant peut également être faite sans programmation. En effet, si les conventions de la programmation "composant" sont respectées, les modèles d'objets d'Oxygène++ et en particulier le modèle GESTION prennent en charge les contrôles minimaux et les opérations de choix d'un élément du composant en lien. (Informations sur les conventions)

Etape 3 : Ajout d'une méthode publique et d'un objet spécialisé

Nous allons maintenant dériver le composant PERSONNE pour lui ajouter une fonctionnalité d'exportation de la liste des personnes. Pour cela nous allons programmer un objet dérivant du modèle EXPORT et compléter l'interface du composant par une nouvelle méthode publique.

Composant PERSONN2 "Le composant PERSONNE enrichi"
Declaration
   BASE : "DOC"
   GLOBAL : "PERSONN2","PERSONNE"
FinDeclaration

Objet PERSONNE.INTERFACE : INTERFACE "Interface enrichie du composant personne"
   Methode EXPORTE
       CreerObjet "PERSONN2"."EXPORTE_LISTE"
   FinMethode
FinObjet

Objet EXPORT : EXPORTE_LISTE "Export de la liste des persones"
   Declaration
       TABLE : "PERSONNE"
       FORMAT : "EXCEL"
       FICHIER_SORTIE : "PERSONNE.XLS"
   FinDeclaration
FinObjet

Explications

Notre source contient maintenant un nouvel objet EXPORTE_LISTE qui dérive non pas d'un objet du composant de base, mais du modèle EXPORT d'Oxygène++. Cet objet est appelé depuis la nouvelle méthode publique EXPORTE de notre objet INTERFACE.

Le composant PERSONNE dispose donc maintenant d'une nouvelle fonctionnalité qui peut être appelée par d'autres composants en ajoutant un simple bouton dans l'interface utilisateur.

Remarque : appel à une nouvelle méthode publique

Notez que la nouvelle méthode publique est directement accessible par l'instruction suivante :

AppliquerMethodeComposant "PERSONNE"."EXPORTE"

Pour que cela soit possible, Oxygène++ utilise une table de redirection ($$.COMPOSANTS) qui permet de rediriger tous les appels fait au composant PERSONNE vers sa version enrichie PERSONN2. Cette table doit être mise à jour au moment de l'installation du logiciel sur le site. En fait, tout se passe comme si le composant PERSONN2 n'existait pas. Les objets qui y font référence continuent à s'adresser au composant de base et en programmation, l'appel suivant ne sera jamais utilisé.

AppliquerMethodeComposant "PERSONN2"."EXPORTE"

Remarque : appel à un nouvel objet

Dans le cas d'un objet, le mécanisme est différent et cela est essentiellement dû au fait que tous les objets du composants (hors INTERFACE) sont considérés comme des attributs privés du composant. En conséquence, l'appel suivant ne fonctionne pas :

CreerObjet "PERSONNE"."EXPORTE_LISTE" //Ne fonctionne pas

Il faut impérativement préciser le nom de l'objet complet ; c'est-à-dire en le préfixant par le nom du composant dans lequel il a été défini.

CreerObjet "PERSONN2"."EXPORTE_LISTE" //Appel correct

Etape 4 : Redéfinition d'une méthode publique et dérivation d'un objet

Composant PERSONN2 "Le composant PERSONNE enrichi"
Declaration
   BASE : "DOC"
    GLOBAL : "PERSONN2","PERSONNE"
FinDeclaration

Objet PERSONNE.INTERFACE : INTERFACE "Interface enrichie du composant personne"
    Methode IMPRIME
        Parametres : Chaine pIdent
        CreerObjet "IMPRIME_LISTE"(pIdent)
    FinMethode
FinObjet

Objet PERSONNE.IMPRIME_LISTE : IMPRIME_LISTE "Impression enrichie"
    Declaration
        PARAMETRES : Chaine pIdent
    FinDeclaration

    Methode DEBUT_TRI
        Si pIdent<>"" Alors
            //On n'imprime qu'un seul enregistrement
            Requete TABLE.IDENT, EgalA, pIdent
        Sinon
            //Pas de filtre, on imprime toute la liste
        FinSi
    FinMethode
FinObjet

Explications :

Dans le source ci-dessus, nous avons redéfini la méthode IMPRIME de l'interface pour quelle tienne compte d'un paramètre éventuel (pIdent). Ce paramètre est retransmis à l'objet IMPRIME_LISTE qui réagit différemment lorsque pIdent en renseigné. L'objet appelé est maintenant en mesure d'imprimer soit la liste des personnes, soit une fiche personne précise dont elle reçoit l'identifiant en paramètre. Il reste à paramétrer l'état utilisé pour adapter le contenu de l'édition en fonction du paramètre reçu.

 

Informations complémentaires

La  dérivation par un exemple