Contrairement aux sous-programmes évènementiels, les sous-programmes non évènementiels sont entièrement écrits par le développeur. Ils ne sont pas destinés à gérer des évènements et leur exécution n'est pas déclenchée par des évènements (ou du moins pas directement).
A priori, les sous-programmes non-évènementiels ne sont pas indispensables. Vous pouvez très bien écrire un projet en mettant tout le code dans les procédures évènementielles, mais vous obtiendrez ainsi un code très long, peu lisible et difficile à mettre à jour.
L'utilisation de sous-programme non-évènementiels est donc surtout une manière de programmer (que je vous conseille !).
Pour illustrer ceci, reprenons le projet Peinture du cours Première Notion.
Rappelons que ce programme permet de calculer la surface à peindre d'une pièce rectangulaire connaissant les dimensions de la pièce (longueur, largeur), le nombre de fenêtres et le nombres de portes.
Cet exemple nous avait justement servi à illustrer le principe du découpage d'un traitement en sous-traitements. Lorsqu'un calcul est trop complexe, on a intérêt à le découper en plusieurs calculs intermédiaires et sauvegarder les résultats intermédiaires dans des variables.
Cela nous avait donné le code suivant pour le bouton Calculer:
private void BT_CalculerActionPerformed (java.awt.event.ActionEvent evt) { // Lecture des données LargeurPiece = es.LireDouble(CT_Largeur); LongueurPiece = es.LireDouble(CT_Longueur); NFenetre = es.LireEntier (CT_NF); NPorte = es.LireEntier (CT_NP); // Calcul de la surface des murs SurfaceDesMurs = 2 * (LargeurPiece * HAUTEUR_MUR + LongueurPiece * HAUTEUR_MUR); // Calcul de la surface des fenetres SurfaceDesFenetres = NFenetre * LARGEUR_FENETRE * HAUTEUR_FENETRE; // Calcul de la surface des portes SurfaceDesPortes = NPorte * LARGEUR_PORTE * HAUTEUR_PORTE; // Calcul de la surface à peindre SurfacePeinture = SurfaceDesMurs - SurfaceDesFenetres - SurfaceDesPortes; // Affichage du résultat es.Afficher(SurfacePeinture,CT_SP); }
Nous avons ici un exemple de code qui peut très facilement se réécrire à l'aide de sous-programmes de la manière suivante:
private void BT_CalculerActionPerformed (java.awt.event.ActionEvent evt) { // Lecture des données LargeurPiece = es.LireDouble(CT_Largeur); LongueurPiece = es.LireDouble(CT_Longueur); NFenetre = es.LireEntier (CT_NF); NPorte = es.LireEntier (CT_NP); // Traitement des données CalculerSurfaceDesMurs (); CalculerSurfaceDesFenetres (); CalculerSurfaceDesPortes (); CalculerSurface_A_Peindre (); // Affichage du résultat es.Afficher(SurfacePeinture,CT_SP); }
Les instructions en blanc sont des appels de sous-programme.
Comparez ce code à celui de la version précédente: il est devenu plus court et plus lisible.
Les instructions permettant de calculer les surfaces (murs, fenêtres, portes, surface à peindre) sont à présent contenues dans des sous-programmes situés dans la zone de déclaration, juste après la déclaration des variables. Voici leur code:
.... double SurfaceDesMurs, SurfaceDesPortes, SurfaceDesFenetres, SurfacePeinture; void CalculerSurfaceDesMurs () { SurfaceDesMurs = 2 * (LargeurPiece * HAUTEUR_MUR + LongueurPiece * HAUTEUR_MUR); } void CalculerSurfaceDesFenetres () { SurfaceDesFenetres = NFenetre * LARGEUR_FENETRE * HAUTEUR_FENETRE; } void CalculerSurfaceDesPortes () { SurfaceDesPortes = NPorte * LARGEUR_PORTE * HAUTEUR_PORTE; } void CalculerSurface_A_Peindre () { SurfacePeinture = SurfaceDesMurs - SurfaceDesFenetres - SurfaceDesPortes; } public Peinture() { initComponents(); } .....
Nous avons ici quatre déclaration de sous-programmes non-évènementiels. Contrairement aux procédures évènementielles, ces déclarations doivent être entièrement écrites par le développeur. Aucune partie du code n'est générérée automatiquement.
Par contre, si on compare ces déclarations à celle de la procédure évènementielle du bouton additionner, on constate que la structure de la déclaration d'un sous-programme non-évènementielle est la même que celle d'un sous-programme évènementiel. On y retrouve l'entête (en bleu) et le corps du sous-programme (lignes en blanc).
Précisons également que les quatre exemples de sous-programmes présentés ici sont très particuliers. Tout d'abord, ils n'ont pas de paramètres (la notion de paramètre d'un sous-programme sera abordé dans la partie du cours consacrée aux procédures) et deuxièmement ce sont tous des procédures. En effet, nous verrons plus loin dans le cours qu'il existe deux catégories de sous-programmes non évènementiels: les procédures et les fonctions. Tous les sous-programmes de notre exemple sont des procédures sans paramètres.
Contrairement à un sous-programme évènementiel, un sous-programme non-évènementiel ne s'éxécute que lorsqu'il est appelé. Pour une procédure, cela signifie qu'il existe quelque part dans le code une instruction d'appel de ce sous-programme. Dans notre exemple, ce sont les quatre instructions en blanc dans le code de la procédure évènementielle du bouton Calculer.
La figure suivante illustre l'exécution du projet Peinture dans sa nouvelle version.
Tant que l'utilisateur n'a pas cliqué sur le bouton Calculer, le programme est dans un état d'attente d'évènement.
Dès qu'il clique sur le bouton, l'exécution de la procédure évènementielle associée à ce bouton est déclenchée.
Dans le corps de cette procédure se trouve tout d'abord l'appel du sous-programme CalculerSurfaceDesMurs.
Cet appel va déclencher l'exécution de ce sous-programme (flèche en rouge).
Lorsque l'exécution de cette procédure est terminée, l'exécution du programme continue à l'instruction qui suit l'appel (flèche en bleu). Dans notre cas, le retour se fait à l'instruction d'appel du sous-programme CalculerSurfaceDesFenetres.
Ce sous-programme sera donc exécuté et au retour ce sera l'appel du sous-programme CalculerSurfaceDesFenetres, etc ... jusqu'à ce que toutes les instructions contenues dans le corps de la procédure évènementielle BoutonAdditonnerActionPerformed soient exécutée.
Lorsque cela sera le cas , le programme retournera automatiquement à un état d'attente d'évènement.
Le mécanisme de retour de sous-programme que nous avons décrit ici s'applique à tous les appels de procédure: après l'exécution d'une procédure, l'ordinateur revient automatiquement à l'instruction qui suit l'appel. Pour les fonctions, nous verrons que le mécanisme de retour est légèrement différent.