Bonnes pratiques

Ce contenu est obsolète. Il peut contenir des informations intéressantes mais soyez prudent avec celles-ci.

Pour clôturer ce cours, je vous propose un petit chapitre d'annexe où nous allons parler de « bonnes pratiques ». Grâce à ce chapitre, vous pourrez prendre de bonnes habitudes dès maintenant. Ce n'est pas après cinq ans de programmation qu'il faudra commencer à se préoccuper de coder et documenter proprement vos projets. Dans un premier temps, nous traiterons donc de la gestion de la mémoire dans Flash, puis nous enchaînerons sur les commentaires de documentation et la documentation avec ASDoc.

La mémoire dans Flash

Flash et la mémoire vive

Tout d'abord, vous devez savoir que tout programme qui se lance doit d'abord se charger en mémoire, et éventuellement charger d'autres fichiers (comme des images). Je parle bien entendu de la mémoire vive de votre ordinateur où sont chargés tous les fichiers nécessaires au fonctionnement de votre système d'exploitation et de tous les logiciels qui fonctionnent sur votre machine.

Le lecteur Flash dispose d'une machine virtuelle qui exécute un langage interprété de haut niveau, ce qui implique que l'ActionScript ne vous permet pas de gérer directement la mémoire mise à la disposition de vos programmes et animations. En effet, c'est le lecteur Flash qui va s'occuper d'allouer de la mémoire et de la vider à votre place (sauf pour quelques exceptions). Bien sûr, il faut respecter certaines règles que nous allons voir plus bas, pour que la mémoire soit libérée correctement, règles qui sont quelquefois bafouées par certains développeurs (d'où certains problèmes récurrents de consommation de mémoire).

Le ramasse-miettes

Flash utilise donc une technique propre aux plateformes d'exécution (comme Java) pour vider le plus efficacement possible la mémoire de l'ordinateur qui n'est plus utilisée par votre animation : le ramasse-miettes (ou garbage collector en anglais). C'est un petit programme qui va se charger de trouver tout ce qui est devenu inutile dans le programme puis d'effacer la mémoire correspondante et ceci régulièrement lors de l'exécution.
Il n'est pas possible de l'appeler manuellement car l'opération peut être complexe et donc relativement lente : ainsi, seul le lecteur a le droit de lancer le ramasse-miettes quand il juge que cela est nécessaire.

Il sera néanmoins possible de l'appeler pendant que vous ferez des tests de débogage lors de l'écriture de vos programmes : il suffit d'entrer cette ligne :
System.gc();
Cette commande sera tout simplement ignorée lors de l'exécution réelle du programme (par exemple sur une page web).

Préparer un objet pour la suppression

Un objet doit remplir un certains nombre de conditions avant de pouvoir être supprimé de la mémoire par le ramasse-miette. On dit alors qu'il est éligible au nettoyage.

Cet objet doit donc :

  • Ne pas être présent dans l'arbre d'affichage de la scène principale.
  • Ne pas avoir de références (variables) pointant sur lui dans d'autres objets.
  • Ne pas avoir d'écouteurs d'événement enregistrés auprès de l'objet en question.

Exemple

Pour illustrer tout ceci, je vous propose à présent un petit exemple. Nous allons réaliser un bouton qui pourrait servir d'accueil pour un site web ou pour un jeu. Une fois le bouton cliqué, celui-ci disparaît pour laisser place à l'application en elle-même. L'objectif est donc de préparer l'objet a sa suppression de la mémoire vive.

Dans un premier temps, créez une classe Button pour définir le graphisme de notre bouton. Celui-ci sera alors composé d'un fond en dégradé, avec un texte par dessus.

Voici le code de cette classe :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package
{
    import flash.display.Sprite;
    import flash.geom.Matrix;
    import flash.display.Shape;
    import flash.display.GradientType;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.text.TextFormat;

    public class Button extends Sprite
    {
        private static const LARGEUR_SCENE:int = 320;
        private static const HAUTEUR_SCENE:int = 240;

        public function Button()
        {
            super();

            // Dégradé de base
            var couleurs:Array = [0xEEBBBB, 0xDD0000];
            var alphas:Array = [1, 1];
            var ratios:Array = [0, 255];

            // Matrice de description
            var matrix:Matrix = new Matrix();
            var largeur:Number = 100;
            var hauteur:Number = 15;
            var rotation:Number = Math.PI / 2;
            var tx:Number = 0;
            var ty:Number = 0;
            matrix.createGradientBox(largeur, hauteur, rotation, tx, ty);

            // Tracé du bouton
            var fond:Shape = new Shape;
            fond.graphics.lineStyle(1, 0x880000);
            fond.graphics.beginGradientFill(GradientType.LINEAR, couleurs, alphas, ratios, matrix);
            fond.graphics.drawRoundRect(0, 0, 100, 25, 10);
            fond.x = (LARGEUR_SCENE - fond.width) / 2;
            fond.y = (HAUTEUR_SCENE - fond.height) / 2;
            addChild(fond);

            // Création du Texte
            var texte:TextField = new TextField();
            texte.autoSize = TextFieldAutoSize.LEFT;
            var format:TextFormat = new TextFormat();
            format.font = "Calibri";
            format.color = 0xFFFFFF;
            texte.defaultTextFormat = format;
            texte.text = "ENTRER";
            texte.selectable = false;
            texte.x = (LARGEUR_SCENE - texte.width) / 2;
            texte.y = (HAUTEUR_SCENE - texte.height) / 2;
            addChild(texte);
        }

    }
}

Aucun problème pour la mise en place du bouton !
Néanmoins, pour illustrer les trois points vus plus haut, nous allons déclarer notre bouton en tant qu'attribut de la classe principale :

1
private var _monBouton:Button;

Nous l'instancions donc, lui ajoutons un écouteur pour l'événement MouseEvent.CLICK, et enfin l'ajoutons à la liste d'affichage, comme ceci :

1
2
3
_monBouton = new Button();
_monBouton.addEventListener(MouseEvent.CLICK, maFonction);
addChild(_monBouton);

Une fois l'application lancée dans le Flash Player, voilà ce que cela donne :

L'objet monBouton devant être supprimé

Une fois le bouton cliqué, nous devons le préparer à sa suppression de la mémoire vive. Dans le cas présent, nous aurons besoin de supprimer l'écouteur enregistré auprès de l'objet, de supprimer celui-ci de la liste d'affichage, mais aussi de supprimer la référence à l'objet définit pour l'attribut. Tout cela sera fait à l'intérieur de la fonction d'écouteur maFonction().

Voilà comment réaliser tout ceci :

1
2
3
4
5
6
7
function maFonction(e:MouseEvent):void 
{ 
    _monBouton.removeEventListener(MouseEvent.CLICK, maFonction);
    removeChild(_monBouton);
    _monBouton = null;
    // L'objet "monBouton" est à présent éligible au nettoyage
}

Voilà !
Ce n'est pas bien compliqué, mais il est vraiment essentiel de réaliser ces opérations pour libérer la mémoire au fil de l'exécution de votre programme. Beaucoup trop de développeurs ont un gros problème de formation à la gestion de la mémoire, ce qui conduit après à des internautes qui pestent parce que « Flash Player prend toute la RAM ». :lol:

Il peut également être intéressant de réutiliser les instances d'objet le plus possible. Par exemple, si vous avez des objets "Tirs de laser", on gagnerait en mémoire (et en temps d'exécution) à les stocker dans un tableau, et à les réutiliser. Au lieu de supprimer chaque tir de laser une fois qu'il est terminé, on peut être plus malin et se contenter de le cacher jusqu'à ce qu'on ai besoin de nouveaux tirs. Cela nous permet d'économiser la création et la suppression de ces objets ! :)

Les commentaires de documentation

Ecrire un commentaire de documentation

Nous avons rencontré dans le TP ce commentaire devant certaines propriétés dans la classe Vecteur2D :

1
2
3
4
5
6
/**
 * Ajoute le vecteur passé en paramètre au vecteur courant.
 * @param vecteur   Vecteur à ajouter.
 * @param apply     Si vrai, modifie directement le vecteur courant. Sinon, renvoie un nouveau vecteur.
 * @return          Le vecteur représentant la somme des deux premiers vecteurs.
 */

Ces commentaires sont des commentaires de documentation. Il permettent à votre IDE d'afficher des informations à propos de ces propriétés lors de l'autocomplétion ou si vous passez la souris dessus. Il sert également à générer une documentation de votre classe à l'aide de l'outil asdoc.

Une infobulle de Flashdevelop tirant des informations du commentaire de document

Pour créer un commentaire de documentation, placez votre curseur de saisie sur la ligne précédent une propriété publique et entrez /** avec deux étoiles. Si vous êtes sur un IDE comme Flashdevelop, il vous sera proposé de générer automatiquement le squelette du commentaire de documentation associé à votre propriété. Par exemple :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/**
 * 
 * @param vecteur
 * @param apply
 * @return
 */
public function ajouter(vecteur:Vecteur2D, apply:Boolean = false):Vecteur2D
{
    // ...
}

A vous ensuite de remplir le commentaire à partir de la deuxième ligne en décrivant l'utilité de la propriété de manière à ce que quelqu'un d'autre que vous puisse comprendre à quoi elle sert. ;)

Voici quelques uns des tags utilisés pour concevoir un commentaire de documentation :

  • @auteur : permet de spécifier l'auteur du code source.
  • @param : permet de faire la description du paramètre spécifié.
  • @return : permet de spécifier la valeur de retour pour la méthode.
  • @default : permet de spécifier la valeur par défaut.
  • @private : permet d'exclure l'élément de la documentation.
  • @see : permet de créer un lien interne ou externe à la documentation.

Bien évidemment, il en existe d'autres, je vous laisserai les découvrir par vous-mêmes !

Générer la documentation de vos classes

À présent, je vais vous montrer comment générer une belle documentation, telle que la documentation officielle.

Pour cela, nous allons avoir besoin d'un projet contenant des classes documentées. Nous pourrions par exemple imaginer une collection de classes permettant de manipuler des éléments mathématiques, tels que des points, des matrices, des vecteurs, …

Voici un exemple de projet contenant un certain nombre de classes :

Un exemple de projet contenant diverses classes

Pour l'exemple, vous remarquerez que j'ai repris les deux classes Outils et Vecteur2D conçues spécialement pour le jeu de billard. Cela nous évitera de perdre du temps dans l'écriture de nouvelles classes, et puis les les commentaires de documentation y sont déjà tout prêts. Je vous invite à en faire autant !

Pour générer la documentation, nous allons utiliser l'outil inclus dans FlashDevelop, tout prêt à être utilisé. Pour le lancer, allez dans le menu Tools, puis cliquez sur Documentation Generator…, comme illustré sur l'image suivante :

Menu Outils

Une fois la fenêtre ActionScript Documentation Generator ouverte, vous devez alors renseigner un certain nombre de champs. Au minimum, vous devez remplir les champs suivants :

  • Page title : il s'agit du titre de la documentation. En général, vous renseignerez simplement le nom de votre projet ou de votre bibliothèque de classes.
  • Output directory : ceci correspond au répertoire qui contiendra l'ensemble des fichiers de votre documentation.
  • Classpaths : vous devez renseigner ici l'ensemble des répertoires où se situe le code source de vos classes.
  • Compiler : étant donné que nous travaillons depuis le départ en Actionscript 3, utilisez plutôt l'option ASDOC (AS3) que celle donnée par défaut.

Voici les données spécifiées pour l'exemple en cours :

Interface du générateur de documentation

Lorsque tout est prêt, lancer le générateur simplement en appuyant sur le bouton Generate!. Si des erreurs se produisent durant la génération des fichiers, on vous le fera savoir via la console de sortie spécifique à l'outil, présent sous l'onglet Output de la fenêtre.

Si tout s'est bien déroulé, vous devriez trouver dans le répertoire de sortie, toute une panoplie de fichiers générés automatiquement. Pour visualiser le rendu, vous devez utilisez le fichier index.hmtl, présent à la racine du répertoire.

Voilà ce que cela donne pour le projet ASDocExample, et plus particulièrement pour la classe Vecteur2D :

La documentation du projet ASDocExample

Les documents générés avec ASDoc sont très populaires au sein de la communauté des développeurs Actionscript. Elles accompagnent très généralement les bibliothèque de classes, telle que l'API Documentation pour la librairie d'interpolation GSAP, librairie d'interpolation créée par Greensock et que nous avons découvert lors de la dernière partie.


En résumé

  • Afin de ne pas saturer la mémoire vive de l'utilisateur de votre application, il est nécessaire de libérer la mémoire au fur et à mesure de l'exécution de celle-ci.
  • Pour être éligible au nettoyage, un objet doit répondre à un certain nombre de critères.
  • Les commentaires de documentation permettent à votre IDE d'afficher des informations concernant les propriétés lors de l'autocomplétion ou lors d'un survol avec la souris.
  • Les commentaires de documentation débutent par la série de caractères /** et utilisent un certain nombre de tags commençant par @.
  • L'ASDoc est un outil permettant de générer automatiquement une documentation, et il est disponible via la fenêtre Documentation Generator dans FlashDevelop.