Les automatismes de composants

La programmation "composant" correspond plus à une nouvelle façon de concevoir son logiciel qu'à une méthode de programmation proprement dite. En effet, programmer "composant" c'est avant tout construire sont logiciel en assemblant des entités indépendantes. Cette approche combinée aux mécanismes de la programmation "Objet" améliore le suivi du développement, la maintenance du produit et la réutilisabilité du code.

 

Parallèlement à cela, nous avons voulu que la programmation de composants soit simple à mettre en Suivre. Oxygène a donc été enrichi par de nouveaux modèles qui bénéficient de puissants automatismes prenant en charge les besoins les plus fréquents. Pour exploiter pleinement ces automatismes, nous avons définis une norme de développement qui est présentée dans ce chapitre au travers d'exemples concrets.

Les conventions à respecter

Pour que les nouveaux modèles exploitent pleinement les automatismes composants le programmeur doit respecter un certain nombre de conventions. Nous les énumérons ici afin que le programmeur ait une vue synthétique des avantages qu'elles apportent.

 

  1. Le composant doit impérativement contenir un objet dérivant du modèle d'objet INTERFACE. Les méthodes publiques de l'interface étant invoquées par les instructions EnvoyerMessageComposant et AppliquerMethodeComposant, etc.

  2. Lorsque le composant stocke des données permanentes, la table principale doit porter le même nom que le composant lui-même. Le respect de cette convention permet d'exploiter les méthodes standards du modèle d'objet INTERFACE avec notamment les méthodes LECTURE et ECRITURE.

  3. Les données d'interface (données publiques du composant) transitent par l'intermédiaire d'une structure d'interface définie dans le descripteur de données. Cette structure doit avoir le même nom que le composant, préfixé par "I_". On aura par exemple une structure de code I_STOCK pour un composant STOCK.

  4. Pour bénéficier de l'attribution automatique d'un identifiant, la table d'interface et la table principale doivent contenir un champ de code IDENT. L'attribution de cet identifiant est prise en charge lors des opérations de création, c'est-à-dire par les modèles d'objets de type GESTION et par la méthode ECRITURE de l'objet INTERFACE.

  5. Dans les objets de type GESTION, les champs de code CODE ;et DESIG sont contrôlés à la validation des créations et des modifications. Les modèles vérifient que les deux champs ne sont pas vides et contrôle l'absence d'homonyme sur le champ CODE.

  6. Les clés étrangères sont de la forme [code composant]_IDENTqui mettent en relation deux composants ont un code normalisé. Prenons l'exemple d'un composant CLIENT en relation avec un composant FAMILLE, la clé étrangère (stockée dans la table CLIENT) doit avoir pour code FAMILLE_IDENT. Cette convention est très importante, elle conditionne les appels aux méthodes publiques CHOIX et GESTION et les contrôles de cohérence de la saisie.

Les explications au travers d'un exemple simple

Pour présenter en détail les automatismes composants, nous allons nous baser sur un exemple qui se limite à la programmation minimale permettant de faire fonctionner un composant CLIENT en relation avec un composant FAMILLE. Chaque client étant associé à 0 ou 1 famille.

Bien évidemment, nous respectons les conventions énoncées au paragraphe précédent. Nous bénéficions donc de l'ensemble des automatismes proposés par Oxygène.

Les tables et les structures d'interface

Le composant CLIENT fournit une table disque CLIENT et une structure d'interface I_CLIENT. Le composant FAMILLE fournit une table disque FAMILLE et une structure d'interface I_FAMILLE. Chaque table et structure d'interface contenant au moins les champs IDENT, CODE et DESIG.

La relation entre le composant CLIENT et le composant FAMILLE est caractérisée la présence dans la table client de la clé étrangère FAMILLE_IDENT.

La programmation minimale du composant CLIENT

Composant CLIENT "Les clients"

Declaration
   BASE : "GESTION"
   TABLE : "CLIENT"
   GLOBAL : "CLIENT"
FinDeclaration

Objet INTERFACE : INTERFACE
FinObjet

Objet GESTION : GESTION "Gestion des client"
   //Utilise l'écran GESTION par défaut
FinObjet

 

La programmation du composant CLIENT se limite à la programmation minimale des objets INTERFACE et GESTION. Comme nous le verrons plus loin, dans notre cas, les automatismes des modèles suffisent amplement.

En pratique, nous conseillons d'isoler chaque objet dans un source (.L4G) différent. 

La programmation du composant FAMILLE

Composant FAMILLE "Les familles de client"

Declaration
   BASE : "GESTION"
   TABLE : "FAMILLE"
   GLOBAL : " FAMILLE "
FinDeclaration

Objet INTERFACE : INTERFACE
FinObjet

Objet CHOIX : CHOIX "Choix d'une famille"
   //Utilise l'écran CHOIX par défaut
FinObjet

Objet GESTION : GESTION "Gestion des familles"
   //Utilise l'écran GESTION par défaut
FinObjet

Même remarque pour le composant FAMILLE, la programmation minimale est suffisante. L'objet CHOIX est ici indispensable car il est utilisé depuis l'écran de la gestion des clients.

L'écran de gestion des clients

L'écran ci-dessous sera exploité par un objet dérivant du modèle GESTION. L'utilisateur sera donc en mesure de consulter, créer et modifier des clients. Il pourra également choisir la famille associée au client en cliquant sur le bouton "Liste" ou en saisissant directement le code de la famille.

Notez que le groupe intitulé "Informations famille" fait référence aux données d'interface du composant FAMILLE. Les deux boutons graphiques permettent respectivement le choix d'une famille et l'appel à la gestion des familles.

L'écran de choix d'une famille de clients

L'écran de choix d'une famille de client est directement exploité par l'objet CHOIX du composant FAMILLE. L'interface utilisateur qu'il propose est conforme à ce qu'attend le modèle à savoir : un bouton permettant de valider le choix, un bouton pour abandonner et un bouton pour choisir "aucune famille". Le bouton gérer permet quant à lui de passer en gestion des familles de client, afin d'en créer de nouvelles ou de modifier celles qui existent.

 

L'appel à la méthode CHOIX

Dans notre exemple, le choix de la famille peut être effectué de deux façons différentes : en cliquant sur le bouton "Liste" ou en tapant sur la touche F4 en étant placé sur la zone I_FAMILLE.CODE. Dans les deux cas aucune programmation n'est nécessaire.

 

Cas n° 1 : Choix en cliquant sur le bouton "Liste" :

Le bouton "Liste" doit avoir le code FAMILLE_CHOISIR

 

Cas n° 2 : Choix en tapant F4 au clavier :

La zone en cours de saisie doit être une donnée publique du composant en lien. C'est le cas de la zone I_FAMILLE.CODE de notre exemple.

 
Voir aussi Méthodes CTRL_BOUTON_x_INTERNE.

Explications

Lorsque l'utilisateur clique sur le bouton "Liste" ou qu'il tape F4 au clavier, la méthode générique correspondante est déclenchée (il s'agit plus précisément des méthodes  CTRL_BOUTON et CTRL_CHOISIR). Le code qui se déroule ici est relativement sophistiqué, surtout lorsqu'il s'agit d'un modèle d'objet de type "gestion".

L'appel à la méthode CHOIX est traité de la façon suivante :

 

1)     Contrôle du respect des conventions :

On vérifie ici que le code du bouton est du type <CodeComposant>+"_CHOISIR" ou que la zone en cours de saisie fait bien référence à une donnée publique du composant en lien. On vérifie également que la clé étrangère <CodeComposant>+"_IDENT" correspondante existe. Si ces conditions ne sont pas respectées, la méthode associée au bouton est déclenchée.

 

2)     On détermine la meilleure stratégie de positionnement :

Il est fréquent que l'utilisateur commence par saisir les premières lettres du code famille, puis fasse appel à la liste pour obtenir de l'aide. Le système exploite les méthodes publiques RECHERCHE_CODE et RECHERCHE_DESIG pour tenter de positionner le choix sur un élément qui respecte la saisie commencée.

 

3)     Appel à la méthode publique CHOIX  :

Par défaut, l'appel à la méthode publique CHOIX (du composant en lien) est prise en charge par le modèle. Si le programmeur souhaite modifier le comportement par défaut, il lui suffit de définir la méthode FAMILLE_CHOISIR et d'y programmer son propre appel. Cette redirection est particulièrement utile lorsque la méthode CHOIX nécessite un appel spécialisé avec par exemple des paramètres supplémentaires. Notez que dans ce cas, il faut impérativement retransmettre l'action de l'utilisateur au modèle par le code suivant :  ErreurARendre = ErreurRendue

 

4)     Récupération de l'action de l'utilisateur :

Quel que soit la méthode d'appel, l'action de l'utilisateur est rendue par l'intermédiaire de la variable système ErreurRendue. Elle vaut 0 si l'utilisateur a choisi un élément, 1 si l'utilisateur a abandonné le choix, 2 si l'utilisateur a fait le choix "aucun". Si l'utilisateur a abandonné, le système restitue le contexte initial et rend la main.

 

5)     On demande l'autorisation de modifier :

Suivant le modèle utilisé, la modification est soumise à conditions. Dans les modèles de type GESTION, il faut anticiper les problèmes de conflits avec d'autres postes (ou d'autres tâches). On fait ici appel à une méthode spécialisée du modèle afin de démarrer la transaction. Cette méthode dépend du contexte ; quand il s'agit de la table principale, la méthode CTRL_DEBUT_MODIF est déclenchée quand il s'agit de la table des lignes (modèles GESTION_DOCUMENT) on fait appel à la méthode CTRL_DEBUT_MODIF_LISTE. Après l'appel, la variable système Saisie permet de savoir si l'opération peut se poursuivre.

 

6)     Récupération du choix de l'utilisateur :

Le choix de l'utilisateur est récupéré par l'intermédiaire de la structure d'interface. On utilise l'instruction LireInterface> pour actualiser le contexte de travail sur les données d'interface.

On met ici à jour la clé étrangère par la valeur de la donnée d'interface de code IDENT.  Dans notre exemple cela correspond à l'opération suivante :

CLIENT.FAMILLE_IDENT = I_FAMILLE.IDENT

On donne ensuite la main à la méthode FAMILLE_IDENT afin que l'objet puisse contrôler la validité du choix effectué et réaliser des opérations complémentaires. L'instruction Controler permet ici de refuser le choix de l'utilisateur et de retourner en saisie.

 

7)     Actualisation de l'écran :

Finalement, tout s'est bien passé ! On réaffiche toutes les zones qui font référence au composant FAMILLE.

L'appel à la méthode GESTION

Bien qu'il soit beaucoup plus simple, l'appel à la méthode GESTION du composant famille fonctionne sur le même principe. L'utilisateur peut cliquer sur le bouton "Gérer" ou tapez au clavier la séquence de touches Alt+F5 en étant positionné sur une donnée d'interface du composant FAMILLE.

On effectue ici un appel asynchrone ; cela signifie que l'objet GESTION du composant CLIENT n'attend aucun résultat de cet appel. La méthode GESTION du composant reçoit en paramètre l'identifiant de la famille courante, qui correspond en fait au champ CLIENT.FAMILLE_IDENT.

 

Contrôle de saisie des données d'interface

Dans l'exemple que nous présentons, il est possible de rattacher le client à une famille en saisissant directement le champ I_FAMILLE.CODE. Cette opération est elle aussi traitée par un automatisme, qui vérifie que la valeur saisie correspond bien à un code reconnu par le composant FAMILLE.

 

Explications :

L'événement générique CTRL_MODIF est déclenché dès que l'utilisateur modifie la valeur d'un contrôle de l'écran. Dans le cas d'une saisie d'informations relatives à un composant en lien, le système prend en charge le contrôle de la saisie de la façon suivante :

 

1)     Contrôle du respect des conventions :

On vérifie ici que le contrôle en cours fait référence à une donnée d'interface et que la clé étrangère <CodeComposant>+"_IDENT" correspondante existe. Si ces conditions ne sont pas respectées, la méthode associée est déclenché.

 

2)     On détermine la meilleure stratégie de positionnement :

Le système traite de façon spéciale les champs CODE et DESIG en exploitant les méthodes RECHERCHE_CODE et RECHERCHE_DESIG du composant en lien. Ces deux méthodes reçoivent en paramètre la valeur saisie par l'utilisateur. L'implémentation par défaut du modèle d'objet INTERFACE recherche le premier enregistrement qui débute par la valeur reçue. Si aucun enregistrement n'est trouvé, le système émet un bip et se repositionne  en saisie sur la zone.

Si le contrôle en cours ne correspond pas aux champs CODE ou DESIG, le système donne la main dans la méthode classique de contrôle de la saisie afin que le programmeur puisse effectuer les vérifications qu'il estime nécessaire.

 

3)     Appel à la méthode LECTURE

Si les opérations de recherche de l'étape n°2 se terminent sans retour d'erreurs, le système fait alors appel à la méthode LECTURE. Il est maintenant en mesure de récupérer l'ensemble des données publiques mises à disposition par le composant.

 

4)     On demande l'autorisation de modifier :

Si une erreur est détectée par les lectures précédentes, l'opération est interrompue avec un bip sonore et on retourne en saisie. Dans le cas contraire, il faut anticiper les problèmes de conflits avec d'autres postes (ou d'autres tâches). On fait ici appel à une méthode spécialisée du modèle afin de démarrer la transaction.

Quand il s'agit de la table principale, la méthode CTRL_DEBUT_MODIF est déclenchée quand il s'agit des lignes (modèles GESTION_DOCUMENT) on fait appel à la méthode CTRL_DEBUT_MODIF_LISTE. Après l'appel, la variable système Saisie permet de savoir si l'opération peut se poursuivre.

 

5)     Récupération des données d'interface :

On utilise l'instruction LireInterface pour actualiser le contexte de travail sur les données d'interface et on met à jour la clé étrangère par la valeur de la donnée d'interface IDENT. Ce qui dans notre exemple correspond à l'opération :

CLIENT.FAMILLE_IDENT = I_FAMILLE.IDENT

On donne ensuite la main à la méthode I_FAMILLE.CODE afin que le programmeur puisse compléter les contrôles effectués et réaliser des opérations complémentaires. L'instruction Controler permet ici de refuser le choix de l'utilisateur et de retourner en saisie.

 

6)     Actualisation de l'écran :

Si la saisie est cohérente, on réaffiche toutes les zones qui font référence au composant FAMILLE et l'utilisateur peut poursuivre son travail.