Constructeur et arguments
Par -Alexandre LEGOUT aka LAlex- le mercredi, mai 10 2006, 15:06 - AS2 - Lien permanent
Lors qu'une classe hérite d'une autre, tout l'interêt est bien évidemment de ne pas réimplémenter les methodes de la classe mère. En effet, en l'absence d'une méthode portant le nom demandé, la méthode de la classe mère est appelée, transmettant ainsi tous les arguments. Eh bien ce qui est dommage, c'est que ce n'est pas le cas du constructeur... :\
En effet, si une classe mère prend un argument en paramètre, et qu'une classe fille hérite de cette dernière sans implémenter de constructeur, les paramètres passés au constructeur de la classe fille ne seront pas transmis.
La preuve par l'exemple: j'utilise actuellement un ensemble de managers gérant chacun des objets (Contacts, Articles, etc...) dont les classes implémentent toutes la même interface. Le but est donc de créer un manager "de base" (non instanciable) qui factorise les comportements communs... Chaque manager est lié à un contexte dans l'application, qui est passé en paramètre au constructeur du manager:
/* == Classes utilisées
Ceci est trés certainement du à la conversion en AS1. En effet, en AS1, déclarer une classe revient à déclarer son constructeur, et donc a relayer "manuellement" les paramètres du constructeur si besoin. Le "compilo" AS2 du coup crée automatiquement un constructeur dont le code est:
interface Item
class Contact implements Item
class Context
*/
// Classe mère
class BaseManager {
private var _context:Context;
private var _selectedItem:Item;
// Le constructeur prend un paramètre
private function BaseManager(ctx : Context) {
_context = ctx;
}
public function getContext() : Context {
return _context;
}
public function getSelectedItem() : Item {
return _selectedItem;
}
private /*protected*/ function setSelectedItem(i : Item) {
if (i != _selectedItem) {
_selectedItem = i;
dispatchEvent({type:"selectionChanged", selected:_selectedItem});
}
}
}
class ContactManager
extends BaseManager {
// Force le typage à 'Contact'
private function setSelectedItem(c : Contact) {
super.setSelectedItem(c);
}
public function getSelectedContact() : Contact {
return Contact(getSelectedItem());
}
}
// Création du contexte
var ctx:Context = new Context("mainApp");
// Création du ContactManager
var contactManager:ContactManager = new ContactManager(ctx);
// Affichage du contexte
trace(contactManager.getContext()); // undefined function ContactManager() {
alors que dans mon cas, il faut que je crée le constructeur moi-même:
super();
}function ContactManager(c : Context) {
super(c);
}
C'est peut-être déjà connu de beaucoup, mais moi j'ai cherché un p'tit moment quand-même... Bref, cela revient encore et toujours au problème d'appeler le constructeur d'une classe mère avec un nombre indéfini d'arguments (l'idéal étant avec apply)... :\
Commentaires
T'as pas lu EAS2 de Moock ? Il me semble qu'il l'explique bien dedans...
Juste heu le coups du private class on est en AS3 là ? Ou j'ai louppé un truc :p
Pour un nombre indéfini d'argument, si tu fais un truc du genre :
super(arguments.slice());ça devrait passer, non ? le problème c'est que dans ce cas tu as aucun typage.
J'ai pas testé donc ça passe peut-être pas. :p
grand-mister > Je l'ai lu il y a un an et demi et je ne me souviens pas forcément de ce qui est dit, je dois avouer que ce n'est pas mon livre de chevet... :p
De plus, les puristes disent qu'un constructeur ne doit pas prendre de paramètres, il est mieux de lui prérférer une méthode init, et bien que je sois d'accord la plupart du temps, il existe à la pratique des cas ou je préfère faire comme ca...
@ali_o_kan> Oups, le private va devant le constructeur en fait...
Changé! 
Franchement nous sommes depuis le départ avec des "fonctions constructeurs"... pour moi le constructeur est justement la fonction qui permet de lancer l'initialisation de l'instance, je vois pas l'intérêt de créer une méthode init() uniquement pour initialiser l'instance ?? cela demande du coup de lancer 2 méthodes pour le prix d'une
Pour ma part je me sert d'une fonction "init" dans mon constructeur uniquement pour initialiser des instructions qui peuvent nécessiter d'intervenir avant l'appel du contenu du constructeur de la SuperClass... en mettant une fonction init dans le constructeur de ma SuperClass cela me permet ensuite de surcharger cette méthode dans la classe qui hérite de la première et de définir certaines méthodes, propriétés etc.... mais à part cela... la fonction constructeur fait très bien l'affaire.
Pour ma part ce qui me dérange en AS2 ... c'est justement que si l'on met rien dans le constructeur le compilateur force l'ajout du super() ! ... Il m'arrive parfois que j'ai vraiment pas besoin de l'initialisation du constructeur de la super classe .... et cela devient du coup compliqué d'éviter son lancement.
J'aurai préféré qu'on soit "obligé" de taper super() dans tous les cas, pour accéder à la super classe...
EKA+
Cela n'est pas une bonne idee. Le constructeur est un mechanisme, inclu dans le language, qui garanti qu'un objet est cree de la bonne maniere avant qu'il ne soit utilise. Utiliser une methode init n'est qu'une convention que d'une part les developeurs ne sont pas obliges de respecter (pourquoi init au lieu de initialize ou setup?) et que d'autres part les utilisateurs de librairies risquent de ne pas utiliser (par faute de documentation ou par simple oubli) et donc d'utiliser des objets improprement initialises (avec les bugs qui s'en suivent bien sur).
Je recommande a ce sujet la lecture du debut du chapitre 6 de "Thinking in C++ " qui explique que le concept de constructeur a ete specialement introduit dans le language C++ pour supprimer une classe d'erreur due a la non-initialisation des structs en C. Le chapitre est en ligne ici:
http://nicolas.blancpain.free.fr/Documents/Cpp/onlinev1/Chapter06.html
Bon, cela dit, Flash est une platforme speciale, du fait que certain objects (components en particulier) peuvent etre places sur le stage sans donner acces a l'appel du constructeur. Dans leur cas, une autre methode est souhaitable pour controller l'initialisation. Perso, je recommande de ne pas utiliser de method init pour toutes les classes qui ne representent pas des objets sur le stage.
Hmm, je me demande si cette situation n'est pas un signe de probleme dans le design? Le constructeur garantit que toutes les parties d'un l'objet sont initialisees correctement. Dans le cas d'une classe derivee, comme certaines parties n'existent que dans la classe mere, le constructeur de la classe mere doit etre appele pour garantir leur initialisation (at ainsi de suite pour toutes les classes meres et grand-meres). Donc a mon avis, c'est un comportement souhaitable qu'un appel au constructeur de la classe mere soit automatiquement introduit dans les constructeurs des classes derivees.
Dans quelles situations souhaites-tu instancier un objet sans appeler le constructeur de la classe mere?
Timoth'
Hello,
pour un nombre d'rguments indéfini, essaie comme ca :
class ContactManager extends BaseManager{
public function ContactManager() {
super.constructor.apply(this, arguments);
}
}
++
c'est trés gamin comme réaction mais je super fière .. je fais exactement pareil.
Ce qui métonne ce que avant en as2 version player 7, le super était ajouter automatiquement,
mais je me suis aperçu dernièrement, (en compilant en player 8, la cause ?) que je n'avait justement pas acces aux variable static définie avec une instance de la classe mère, le fait de mettre le super() a la main corrige le problème, j'en conclu donc que le super n'est plus mis par defaut aux moment de la compilation.
enfin j'ai pas poussé les testes plus que ca.
Dans ton cas :
class ContactManagerextends BaseManager {
function ContactManager(c : Context) {
super(c);
}
// Force le typage à 'Contact'
public function getSelectedContact() : Contact {
return Contact(getSelectedItem());
}
}
devrait suffir, je ne parle pas du constructeur, qui me paraît... nécessaire d'être remis, mais de ta fonction setSelectedContact...
Contact implements Item non ?
alors ça ne sert à rien de recréer une fonction pour bien dire que c'est un Contact en paramètre, alors que tu le passes directement en Item dans la fonction et que tu n'appliques rien d'autre...
pour le retour avec getSelectedContact, je comprends... (bon, il manque une gestion à côté, mais c'était qu'un exemple je pense)
@xion> En effet, ce n'est qu'un exemple...

Le typage de setSelectedItem est tout à fait cohérent ici. En effet, le private n'est qu'un "protected", donc je veux que si quelqu'un hérite de ContactManager il ne puisse pas passer n'importe quelle classe qui implémente Item à cette méthode...
D'ailleurs, dans la pratique, Contact est aussi une interface en fait, j'ai juste voulu éviter de compliquer le code...
++ ^^
Fil des commentaires de ce billet