Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

Passer de la POO en AS1 à la POO en AS2

Compatible ActionScript 2. Cliquer pour en savoir plus sur les compatibilités.Compatible ActionScript 1 et 2. Utilise des techniques dépréciées. Cliquer pour en savoir plus sur les compatibilités.Par LAlex

Article issu du forum, écrit par LAlex le 10 sept 2003

Prérequis

  • Connaitre les principes de la POO
  • Savoir créer une classe en ActionScript 1

Présentation

Tous les programmeurs POO en Flash MX ne sont pas forcément habitués à des langages objets tels Java ou C++. Dans sa nouvelle version MX 2004, Macromedia introduit ActionScript 2, qui a pour but, entre autre, de ralier à sa cause les programmeurs utilisant des langages “standards”, en utilisant une syntaxe trés proches de celle dont ils ont l'habitude. Pour les autres, voici comment passer en douceur à l'ActionScript 2.0.

1. Différences des concepts

Si vous vous interessé à ce qui se passe dans le monde de l'ActionScript (nous utiliserons l'abbréviation “AS” dans le tutoriel) vous avez peut-être deja lu ou entendu que l'AS de Flash MX permet de faire de la POO basé sur les prototypes (prototype based). A l'inverse, AS2 propose de faire de la POO basée sur les classes (class based). Voyons quels sont ces deux principes:

POO avec les prototypes

Pour créer une classe, on crée en fait une fonction qui sera la fonction contructeur de la classe. Ce n'est pour l'instant rien d'autre qu'une fonction. C'est le fait de l'utiliser avec l'opérateur 'new' qui en fait une classe.

Pour rajouter une méthode à un objet, on va alors créer des fonctions dans son prototype. Lorsque l'on va appeler une méthode de l'objet, cet appel va vérifier si la méthode existe dans l'objet. Si ce n'est pas le cas, elle va regarder si elle existe dans le prototype du constructeur (puis dans le prototype du prototype, etc…) : c'est ce que l'on appelle la “chaîne de prototype”. C'est comme cela qu'est gérée le fait que plusieurs objets puissent avoir la même méthode. En effet, si tous les objets ont utilisés le même constructeur, le prototype du constructeur sera le même pour tous ces objets. Pour faire de l'héritage, on rajoute tout simplement un prototype à l'endroit voulu dans la chaine de prototype. Il existe d'autres utilisations avancées des prototypes, mais nous ne somme pas la pour ca …

POO avec les classes

La POO avec les classes exclu totalement la notion de prototypes. Les classes sont déclarées en tant que telles : c'est à dire que les classes ne sont pas des fonctions, mais bien des classes à part entière. C'est également dans la déclaration de la classe que nous allons créer un constructeur, les méthodes, les propriétés, etc… Une fois tout cela effectué, la classe peut être instanciée de la même manière, en utilisant l'instruction 'new'. La POO basée sur les classes simplifie également l'héritage, et propose de possibilités supplémentaires que nous verrons plus en détail une peu plus loin dans ce tutoriel.

2. Organisation du code en AS2

Chaque classe créé avec AS2 doit se trouver dans un fichier .as qui porte le même nom que la classe. Vous ne pouvez pas déclarer plusieurs classes dans un même fichier .as et si le nom du fichier est différent de celui de la classe, la classe sera tout simplement ignorée (et donc impossible à instancier).

Flash MX 2004 offre plusieurs moyens d'accéder aux classes situées dans les .as

  1. Les fichiers .as sont situés dans le même répertoire que le fichier .fla au moment de la compilation.
  2. Les fichiers sont situés dans des sous-répertoires de l'emplacement du fichier .fla et il vous faudra utiliser la commande import dans votre animation pour y accéder. Pour ceux qui connaissent, cela s'aparent au import de Java et au using de C# … les répertoires deviennent une simulation des packages.
  3. Les fichiers .as sont dans des repertoires qui ont été rajoutés dans les paramètres de publication d'AS2 : Menu Fichier » Paramètres de publication … » Onglet [Flash] » Bouton [Paramètres…]. Ces répertoires peuvent également être utilisés comme point de départ pour les instructions import.

3. Portage du code de AS1 vers AS2

Les “traductions” de code qui vous sont présentées dans cette partie se limitent à offrir exactement les même fonctionnalités en AS1 que dans leur version AS2. Nous aborderons les spécificités de l'AS2 dans les parties suivantes.

Définir une classe

Créons une classe en AS1 que l'on va nommer Individu. Nous allons la créer dans _global, afin qu'elle soit accessible de partout. Conformément à ce que nous explique fred dans son tutoriel, nous allons la déclarer comme suit :

_global.Individu = function (nom, prenom, date) {
 
	this._nom = nom;
	this._pre = prenom;
	this._date = date;
 
}

Avec ce code, pour créer un individu, nous allons devoir lui passer un nom, un prénom et une date de naissance. Comme nous en avons l'habitude, le constructeur va s'occuper de créer les propriétés de l'objet.

Pour créer cette même classe en AS2, nous allons utiliser l'instruction 'class' dans un fichier nommé 'Individu.as', et créer son constructeur à l'interieur de la déclaration de classe. Voici ce que cela donne :

class Individu {
 
	var _nom;
	var _pre;
	var _date;
 
	function Individu(nom, prenom, date) {
 
		_nom = nom;
		_pre = prenom;
		_date = date;
 
	}
 
}

Vous remarquerez qu'en AS2, c'est dans la définition de la classe que l'on spécifie quelles sont ses propriétés à l'aide de l'instruction var. La fonction constructeur est une fonction qui porte le même nom que la classe, et elle se content d'assigner les valeurs aux propriétés (mais ne les crée pas). Si l'on n'avait pas déclaré les propriétés, un message d'erreur se serait affiché au moment de la compilation. Vous remarquerez également que nous n'avons pas utilisé le sacro-saint ciblage. En effet, il est rarement utilisé dans les langages “classiques”, mais peut l'être lorsqu'il y a ambiguité sur la provenance de la variable. Le this existe donc toujours, mais est moins utilisé.

Les méthodes

Aprés avoir créé notre classe Individu en AS1, nous allons vouloir créer ses méthodes. Pour cela, comme expliqué dans la description de la POO “prototype based”, nous allons rajouter une fonction au prototype du constructeur aprés la création du constructeur :

// Creation du constructeur
 
_global.Individu = function (nom, prenom, date) {
 
	this._nom = nom;
	this._pre = prenom;
	this._date = date;
 
}
 
 
 
// Creation de la methode
 
Individu.prototype.afficher = function() {
 
	trace("L'individu s'appelle " + this._pre + " " + this._nom + " et est né(e) le " + this._age + " ans.");
 
}

Voyons maintenant comment créer cette méthode dans le cadre d'un déclaration de classe en AS2. Cette méthode va étre écrite à l'intérieur de la déclaration de classe :

class Individu {
 
	var _nom;
	var _pre;
	var _date;
 
 
	// Fonction constructeur
 
	function Individu(nom, prenom, date) {
 
		_nom = nom;
		_pre = prenom;
		_date = date;
 
	}
 
 
 
	// Methode afficher
 
	function afficher() {
 
		trace("L'individu s'appelle " + _pre + " " + _nom + " et est né(e) le " + _date + " ans.");
 
	}
 
}

Le addProperty et son remplacant

Dans les langages objets standards, les accés aux propriétés se font généralement uniquement au moyen des méthodes. addProperty est une spécificité de Flash, qui permet plus de confort pour accéder aux propriétés d'une classe. Nous pourrions ici créer une propriété “virtuelle”, qui donnerait le sexe de l'individu. Il faut donc créer une méthode qui va retourner le sexe de l'individu. Considérons que pour une raison qui nous est propre, nous voulons stocker le sexe sous la forme d'un chiffre (1 pour les hommes, 2 pour les femmes), mais nous voulons y accéder avec des lettres (M pour les hommes et F pour les femmes). Il nous suffit alors de créer une propriété virtuelle “sexe” :

// Creation du constructeur
 
_global.Individu = function (nom, prenom, date) {
 
	this._nom = nom;
	this._pre = prenom;
	this._date = date;
	this._numSexe = 0;
 
}
 
 
 
// Methode qui retourne le sexe de l'individu
 
Individu.prototype.getAlphaSexe = function() {
 
	switch(this._numSexe) {
 
		case 1 :
		return "M";
 
		case 2 :
		return "F";
 
		default :
		return "Inconnu";
 
	}
 
}
 
 
 
// Methode qui définit le sexe de l'individu
 
Individu.prototype.setAlphaSexe = function(lettre) {
 
	switch(lettre.toUpperCase()) {
 
		case "M" :
		this._numSexe = 1;
		break;
 
		case "F" :
		this._numSexe = 2;
		break;
 
		default :
		this._numSexe = 0;
 
	}
 
}
 
 
 
// Ajout de la propriété 'sexe'
 
with(Individu.prototype) {
 
	addProperty("sexe",getAlphaSexe,setAlphaSexe);
 
}

Nous avons une propriété “virtuelle” nommée 'sexe', qui va contenir soit “M”, soit “F”, soit “Inconnu” … alors que notre objet stocke le sexe de l'individu sous la forme 0, 1 ou 2 …

Avec AS2, nous allons pouvoir créer des méthodes qui seront considérées directement comme les méthodes get et set d'une propriété “virtuelle”. Il suffit de rajouter le mot-clé 'get' ou 'set' avant le nom de la méthode :

class Individu {
 
	var _nom;
	var _pre;
	var _date;
	var _sexe=0;
 
 
	// Fonction constructeur
 
	function Individu(nom, prenom, date) {
 
		_nom = nom;
		_pre = prenom;
		_date = date;
 
	}
 
 
 
	// Methode qui retourne le sexe
 
	function get sexe() {
 
		switch(_numSexe) {
 
			case 1 :
			return "M";
 
			case 2 :
			return "F";
 
			default :
			return "Inconnu";
 
		}
 
	}
 
 
 
	// Methode qui définit le sexe
 
	function set sexe(lettre) {
 
		switch(lettre.toUpperCase()) {
 
			case "M" :
			_numSexe = 1;
			break;
 
			case "F" :
			_numSexe = 2;
			break;
 
			default :
			_numSexe = 0;
 
		}
 
	}
 
}

Et voila, nous avons effectué exactement la même chose que le code précédent en AS1, c'est à dire que nous pourrons accéder à monIndividu.sexe en utilisant “M” et “F”, sauf que le addProperty n'a plus aucune utilité !

L'héritage

En AS1, l'héritage est assez compliqué à mettre en oeuvre, car soit basé sur la méthode de Macromedia qui est assez discutable en terme d'optimisation et de logique (mais que nous allons utiliser ici par soucis de clarté), soit basé sur du code plus complexe qui exploite la chaîne de prototypes. De plus, étant donné que l'héritage se fait aprés la création du constructeur, les chances de se perdre dans le code sont assez importantes. Nous allons voir ici que l'héritage en AS2 est devenu simplissime.

En AS1, pour créer une classe Membre (avec login et mot de passe), qui va hériter de la classe Individu, nous allons écrire un code qui va ressembler à ca :

// Constucteur de la classe Individu
 
_global.Individu = function(nom, prenom, date) {
 
	this._nom = nom;
	this._pre = prenom;
	this._date = date;
 
}
 
 
 
// Constructeur de la classe Membre
 
_global.Membre = function(nom, prenom, date, login, pass) {
 
	// On appelle le constructeur de la classe mere
	super(nom, prenom, date);
	this._login = login;
	this._pass = pass;
 
}
 
 
 
// On fait heriter Membre de Individu
 
Membre.prototype = new Individu();

Les instances de la classe Membre héritent ainsi des propriétés/méthodes de la classe Individu, plus celles propres à la classe Membre.

En AS2, l'héritage se fait le plus simplement du monde en utilisant aprés le nom de la classe l'instruction 'extends' suivi de la classe mère. N'oubliez pas que les deux classes sont dans deux fichiers différents !!!

// ===== Individu.as =====
 
 
 
// Declaration de la classe Individu
 
class Individu {
 
	var _nom;
	var _pre;
	var _date;
 
 
 
	// Fonction constructeur
 
	function Individu(nom, prenom, date) {
 
		_nom = nom;
		_pre = prenom;
		_date = date;
 
	}
 
}
 
 
 
// ===== Membre.as =====
 
class Membre extends Individu {
 
	var _login;
	var _pass;
 
 
	function Membre(nom, prenom, date, login, pass) {
 
		super(nom, prenom, date);
		_login = login;
		_pass = pass;
 
	}
 
}

Et voila, difficile de faire plus simple !!! 8)

4. De nouvelles habitudes avec AS2

ActionScript 2 introduit des nouveautés qui n'étaient pas accessibles avec AS1, ou alors cela necessitait du code souvent lourd et trés complexe. Ces fonctionnalités deviennent quasi-obligatoires à partir du moment où l'on veut développer un code clair et rigoureux, et se doivent donc d'être utilisées dorénavant. Les exemples donnés uniquement à titre indicatif dans le paragraphe précédent ne les intégrant pas, il ne doit pas être utilisé tel quel. Voici les nouveaux concepts importants d'AS2 :

Le typage fort consiste à donner un type à une variable, et à lui interdire de contenir un type différent que celui qui lui a été imposé. Précédemment, il était tout à fait possible d'assigner un nombre dans une variable, puis à la ligne suivante de lui assigner une chaîne de caractères. Ce qui fait que lire un code oublié, ou fait par quelqu'un d'autre devient assez difficile s'il ne contient pas les typages …

Concrétement, AS2 permet de typer les variables, les paramètres des méthodes, et la valeur de retour des méthodes. C Le typage se fait en mettant deux points ( : ) aprés la déclaration de la variable, suivi de la classe qui indique le type de la variable. Pour les valeurs retournées par les méthodes, les deux points se mettent aprés la parenthèse de fermeture des arguments. Reprenons la classe Individu créée précédemment :

class Individu {
 
	// La déclaration des propriétés se fait avec le typage
 
	var _nom : String;
	var _pre : String;
	var _date : String;
	var _numSexe : Number = 0;
 
 
 
	// Constructeur avec typage des arguments
 
	function Individu(nom:String, prenom:String, date:String) {
 
		_nom = nom;
		_pre = prenom;
		_date = date;
 
	}
 
 
 
	// Methode qui renvoie le sexe avec typage de la valeur de retour
 
	function get sexe():String {
 
		switch(_numSexe) {
 
			case 1 :
			return "M";
 
			case 2 :
			return "F";
 
			default :
			return "Inconnu";
 
		}
 
	}
 
 
 
	// Le reste du code suit la meme logique ...
 
 
 
}

Les propriétés et méthodes publiques et privées

Les propriétés et méthodes d'une classe n'ont pas toutes besoins d'être accédées depuis l'extérieur de l'objet. Cela peut parfois être dangeureux d'accéder directement à une propriété, car certaines instructions peuvent ne pas être executées. AS2 offre la possibilité d'empecher l'accés de l'extérieur à certaines propriétés ou méthodes. Ainsi, seules les méthodes de l'objet peuvent accéder à ces dernières.

Ce concept est trés important, car c'est lui qui garantit l'encapsulation de vos données, ainsi que leur intégrité, surtout lors de développements en travail collaboratif, ou chacun peut-être amené à utiliser les classes d'un autre.

Concrétement, toute propriété/méthode qui ne spécifie pas son “accessibilité” est considérée implicitement comme étant publique. Il est quand même préférable d'éviter ce qui est implicite, et de spécifier clairement ce qui est public et ce qui est privé, excepté pour le constructeur qui est toujours public. Pour cela, on utilise les mots clés 'public' ou 'private'avant la déclaration de la porpriété ou de la méthode. Reprenons notre classe Individu :

class Individu {
 
	// nom, prenom et date sont accessibles
 
	public var _nom : String;
	public var _pre : String;
	public var _date : String;
 
 
 
	// le sexe en numerique ne peut pas être accédé de l'exterieur
 
	// On utilisera plutot les methodes get et set ...
 
	private var _numSexe : Number;
 
 
 
	// Pas besoin de déclaration explicite pour le constructeur
 
	function Individu(nom:String, prenom:String, date:String) {
 
		_nom = nom;
		_pre = prenom;
		_date = date;
 
	}
 
 
 
	// On peut aussi créer des méthodes privées
 
	private function maMethodePrivee():Number {
 
		// Cette méthode ne peut être utilisée que par une autre
 
		// méthode du même objet
 
		// ....
 
	}
 
}

Les propriétés et méthodes statiques

Les propriétés et méthodes statiques sont partagées par toutes les instances d'un même classe. Il n'est même pas nécessaire d'avoir une instance de cette classe pour y accéder. On utilise alors la syntaxe nomDeLaClasse.nomDeLaProp …

Les méthodes/propriétés statiques sont déclarées au moyen du mot-clé static, situé aprés la déclaration public/private, et avant l'instruction var/function … Si la déclaration d'un propriété statique est accompagné d'une assignation de valeur, cette assignation n'est executée qu'une fois, au moment de la déclaration de la classe (et non pas à son instanciation).

Une utilisation fréquente des propriétés statiques peut être de compter le nombre d'individus :

class Individu {
 
	// Propriétés contenant le nombre total d'individus
 
	// Elle est mise à zéro la première fois
 
	public static var nbIndividu:Number = 0;
 
 
 
	// Propriétés non statiques
 
	public var _nom : String;
	public var _pre : String;
	public var _date : String;
	private var _numSexe : Number;
 
 
 
	// Constructeur de la classe
 
	function Individu(nom:String, prenom:String, date:String) {
 
		_nom = nom;
		_pre = prenom;
		_date = date;
 
		// A la création d'un individu, on augmente la propriété statique de 1
 
		nbIndividu++;
 
	}
 
}

On pourra alors accéder à cette propriété soit en appelant nbIndividus depuis l'intérieur d'un méthode (comme cela est fait dans le constructeur, soit en tapant :

trace("Nombre d'individus créés : " + Individu.nbIndividus);

Dans ce cas précis, le problème est que nous ne pouvons pas réduire le nombre d'individus quand un individu est supprimé, et cela parce qu'il n'est pas possible de créer un destructeur en AS2 …

Les interfaces

Comme expliqué dans le tutoriel sur les concepts de la POO, les interface sont un peu similaires à des classes, sauf que l'on ne peut pas créer d'instance d'un interface, et que les méthoeds ne contiennent pas de code. Elle sont en fait un “contrat”, que les classes qui en découlent devront impérativement remplir. Les avantanges des interfaces sont importants lorsqu'il s'agit de travailler en équipe, ou constituent tout simplement une aide à la compilation, afin d'être sur que certaines classes ont été correctement implémentées. Si dans une classe dérivée d'une interface, il manque une méthode de l'interface, le compilateur va signaler une erreur. De la même manière, si la méthode de la classe et celle de l'interface n'ont pas la même signature (nombre et type des arguments + type du retour), le compilateur va également signaler une erreur …

Un autre aspect des interfaces est que l'on peut créer une classe à partir de plusieurs interfaces. Cela permet de s'assurer que la classe va bien avoir les fonctionnalités qui sont décrites dans les interfaces …

On ne peut pas tout faire dans une interface AS2 :

  • On ne peut pas déclarer de propriétés
  • On ne peut déclarer que des méthodes publiques
  • On ne peut déclarer de méthodes statiques
  • Une interface ne contient pas de constructeur
  • Une interface ne peut pas implémenter une autre interface
  • Une interface ne peut pas hériter d'une classe

Pour déclarer une interface, on utilise la même syntaxe que pour les classes, sauf que l'on utilise le mot clé interface au lieu de class. Pour dériver une classe d'un interface, on utilise l'instruction implements aprés l'instruction d'héritage. Ensuite, on donne la liste des interfaces implémentées par la classe, separées par des virgules. Concrétement, voici un exemple de deux interfaces (toujours chacune dans un fichier .as différent) et d'un classe qui les implémente. On veut que l'individu soit un humain (il doit donc savoir parler et marcher par exemple), et l'on veut aussi qu'il soit un coureur (il doit donc savoir courir) :

// ===== Humain.as ====
 
interface Humain {
 
	public function marcher(nbPas:Number);
 
	public function parler(sujet:String):String;
 
}
 
 
 
// ===== Coureur.as =====
 
interface Coureur {
 
	public function courir(distance:Number);
 
}
 
 
 
// ===== Individu.as =====
 
class Individu implements Humain, Coureur {
 
	// Declaration des propriétés
 
	var _nom:String;
	var _pre:String;
	var _date:String
 
 
	// Constructeur de la classe
 
	function Individu (nom:String, prenom:String, date:String) {
 
		// Code du constructeur
 
	}
 
 
 
	// On écrit le code des méthodes décrites par les interfaces
 
	// Interface Humain
 
	public function marcher(nbPas:Number) {
 
		// Instructions pour faire marcher l'individu
 
	}
 
 
 
	public function parler(sujet:String):String {
 
		// Instructions pour faire parler l'individu
 
		// sur un sujet donné. La phrase qu'il doit prononcer
 
		// est retournée par la méthode
 
	}
 
 
 
	// Interface Coureur
 
	public function courir(distance:Number) {
 
		// Instruction pour faire courir l'individu
 
		// sur une distance donnée
 
	}
 
}

Dans ce cas précis, si l'on avait pas créé les méthodes marcher, parler et courir, ou si l'on avait créé des méthodes qui possédaient une signature différente de celles données dans les interfaces, le compilateur aurait signalé une erreur.

Les classes "dynamiques"

Comme cela a été dit dans la partie sur les déclarations de classes, il faut déclarer les propriétés d'une classe. Seulement, il peut arriver que l'on ai besoin de rajouter une propriété dans un objet, sans qu'elle ai forcément été déclarée avec la classe. Il faut alors autoriser ce type d'action au moment de la déclaration de la classe, au moyen du mot clé dynamic, situé devant le mot clé class.

Voici un exemple de script qui génère une erreur, et le même script avec une classe dynamique qui ne génère pas d'erreur :

// Le constructeur va provoquer une erreur de compilation
 
class Individu {
 
	var _nom:String;
	var _pre:String;
	var _age:Number;
 
 
	function Individu(nom:String, prenom:String, age:Number) {
 
		_nom = nom;
		_pre = prenom;
		_age = age;
 
		// La propriété _statut n'a pas été déclarée
 
		// La compilation va retourner une erreur
 
		if (age < 18) {
 
			_statut = "mineur";
 
		} else {
 
			_statut = "majeur";
 
		}
 
	}
 
}
// La classe etant dynamique, le contructeur ne provoque pas d'erreur
 
dynamic class Individu {
 
	var _nom:String;
	var _pre:String;
	var _age:Number;
 
 
	function Individu(nom:String, prenom:String, age:Number) {
 
		_nom = nom;
		_pre = prenom;
		_age = age;
 
 
 
		// La propriété _statut n'a pas été déclarée
 
		// mais la classe est dynamique, donc pas d'erreur
 
		if (age < 18) {
 
			_statut = "mineur";
 
		} else {
 
			_statut = "majeur";
 
		}
 
	}
 
}

Les propriétés ainsi créées “a la volée” sont automatiquement publiques.

5. Conclusion

Comme vous l'avez sûrement constaté, ActionScript 2 apporte non seulement une lisibilité et une organisation bien plus grande de votre code et en facilité grandement la réutilisation.

Mais en plus de cela, ses possibilités résolument proche des langages objets standards tels que Java ou C# vous permettent aussi de créer un code plus robuste, qui s'il est bien fait, limite énormément les possibilités d'erreur à l'utilisation des classes que vous avez crées : une fois une classe créée et débuguée en AS2, vous pouvez la réutiliser à volonté, sans vous poser la question de savoir ou se situe l'erreur : elle est dans l'utilisation des classes, et non pas dans le code des classes …

La facilité d'utilisation de l'héritage est un des gros points forts de l'AS2. Pensez-y avant de modifier une classe que vous avez déjà développé : étendez la plutôt au moyen d'une autre classe qui en hérite …