Interfaces et héritage : compilo : 1 - LAlex : 0
Par -Alexandre LEGOUT aka LAlex- le 28 juillet 2004, 18:29 - AS2 - Lien permanent
Un aspect singulier des interfaces face à l'héritage est que le compilateur de Flash MX 2004 ne se contente pas de vérifier si une classe implémente l'interface, mais il vérifie aussi que les méthodes devant être implémentées le sont ...
Pour etre plus clair, j'ai découvert cela en faisant des tests en réimplémentant ma bibliothèque sur les matrices. Ma classe Matrix hérite d'un classe Grid. Cette classe Grid possède une méthode clone(). J'ai voulu créer une interface IClonable pour m'assurer qu'une matrice puisse être clonée, et ayant oublié de créer la méthode, je me suis rendu compte que le compilateur laissait passer malgré tout ... :o En fait, cette méthode existe déjà dans Grid, alors que Grid n'implémente pas IClonable ! ![]()
Voici le code que le compilateur laisse passer :/*
* interface IClonable
*/
interface net.lalex.interfaces.IClonable {
public function clone():Object;
}
/*
* classe Grid
*/
class net.lalex.data.Grid extends Array {
// Plein de code ...
// Retourner un clone de la grille
public function clone():Object {
var ret:Grid = new Grid(_gridWidth, _gridHeight);
ret.copyFrom(this);
return ret;
}
// Copy les éléments d'une autre grille
public function copyFrom(g:Grid):Void {
// Code
}
}
/*
* classe Matrix
*/
class net.lalex.maths.Matrix extends net.lalex.data.Grid implements net.lalex.interfaces.IClonable {
// Aucune méthode 'clone()'
}
Normal allez-vous me dire ? Oui, en effet.
Mais je ne pensais pas que le compilateur allait si loin. Ca ne m'arrange pas en fait, car j'aurais bien voulu forcer la classe Matrix à surcharger la méthode clone() ... ![]()
Commentaires
salut,
En faite est que faudrait pas que ce soit Grid qui doivent implementer Iclonable plutot ?
Parce que meme si d'un point de vue logique de compilo ca passe... je trouve bizarre d'implementer une interface et de pas redéfinir les méthodes de l'interface dans la classe en question...
Je vois plutot l'interface comme un moyen de dire
Attention cette classe doit avoir SA méthode clone (et pas celle de la super classe)
En tout cas c'est bien intéressant.
a+
ben elle l'a SA méthode clone, elle l'a emprunter de SA super classe!
Sauf erreur de ma part
il est conceptuellement impossible de FORCER une surcharge de methode en AS2 sauf ... si tu utilise des classes abstraites !!
Donc dans ton cas Matrix doit etendre une class Abstraite qui etend elle meme Grid qui etend ton interface
Je crois avoir vue une explication des classes abtraites et la facon d'en faire sur le blog de tweenPix car AS2 ne te permet pas directement d'en creer il me semble.
J'espere avoir ete clair.
En effet, AS2 ne permet pas de faire des classes abstraites, et je connais la méthode de petepx !
Mais dans ce cas précis, elle ne pourras pas fonctionner, étant donné que le compilateur parcoure toute la chaine d'héritage. Le méthode de petepx se base sur le fait qu'on puisse utiliser une propriété en tant que fonction ...
De plus, je vais avoir un sacré panel d'héritage, alors je me vois mal faire un classe abstraite entre chaque héritage. Ce serait déjà trés lourd au niveau code (en AS2, une classe abstraite = une classe + une interface) pour pas grand chose, mais également beaucoup moins performant, étant donné la longueur de la chaine de prototypes à parcourir si on veut utiliser les méthodes de la classe la plus haute (dans mon cas : Grid) ...
Tant pis ...
une Interface c'est juste un contrat
si telle Interface possede la methode "clone"
alors le contrat est que chaque class implementant cette Interface doit posseder une methode "clone"
que la methode soit directement implémentée dans la class ou héritée ou autre, le contrat est respecté
donc normal qu'il n'y ait pas d'erreur de compilation.
par contre dans ton code attention a ca:
ret.copyFrom( this );
est-ce que le "this" est dans le contexte de la class Grid ou de la class Matrix ?
par contre là où tu risque d'avoir des problemes c'est quand tu lanceras "clone" depuis la class Matrix
qui appelera "copyFrom" et que l'argument de copyFrom demandera un type Grid (a moins bien sur que tu implementes une methode copyFrom specialement pour la class Matrix)
et attention aussi a bien faire la difference entre un "clone" et un "copy",
une "shallow copy" et une "deep copy" c'est TRES different ;).
tu peux en dire + là dessus
zwetan > J'ai bien compris ca ! En fait, la classe Grid est un tableau à 2 dimensions. Je me contente donc de recopier les valeurs de chaque case en bouclant sur le tableau d'origine avec un simple =. Il s'agit donc d'une copie "simple" (ce que tu nommes "shallow copy"). Pour la grille, c'est pas forcément l'idéal. Mais pour Matrix, je teste le typage des éléments, donc ca ne contient que des Number ... Donc, la différence entre "shallow" et "deep" n'existe pas dans ce cas !
Enfait, histoire de récupérer un bon type, et de ne pas avoir besoin de recoder "clone" pour chaque classe, j'ai crée une méthode privée newClone(), qui renvoie une nouvelle instance avec les bonnes dimensions. La méthode clone() appelle ensuite cette méthode newClone dans le bon contexte, ce qui me permet d'avoir un clone bien typé. Je ne me souvient plus du nom de ce pattern, qui requiert une implémentation dans les sous-classes, appelée par une méthode de la superclasse ...
Voici l'état actuel du code :
class net.lalex.data.Grid extends Array implements net.lalex.interfaces.ICloneable {// Constructeur, etc...
private function newClone():Object {
return new Grid(_gridWidth, _gridHeight)
}
// Retourner un clone de la grille
public function clone():Object {
var ret:Grid = Grid(newClone());
ret.copyFrom(this);
return ret;
}
// Copy les éléments d'une autre grille
public function copyFrom(g:Grid):Void {
for (var i:Number = 0 ; i < _gridWidth ; i++) {
for (var k:Number = 0 ; k < _gridHeight ; k++) {
this[i][k] = g[i][k];
}
}
}
}
class net.lalex.maths.Matrix extends Grid implements ICloneable {
// Code, etc...
private function newClone():Object {
return new Matrix(_gridHeight, _gridWidth);
}
}
eka > En gros, la différence, c'est est-ce que tu clones les références aussi, ou juste l'objet, dont les références seront les mêmes ...

Si ton objet contient des objets en tant que propriété, ton clone doit-il cloner ces objets, ou pointer vers les mêmes ? C'est toute la différence. On peut dire que la "deep copy" est un clonage recursif ...
D'ailleurs, voila un exemple AS1 de "deep copy" : http://www.shockwave-india.com/blog/actionscript/?asfile=Object-clone.as
Facile quand on a pas les contraintes du typage !
[quote="LAlex":1f07931d13]Je ne me souvient plus du nom de ce pattern, qui requiert une implémentation dans les sous-classes, appelée par une méthode de la superclasse ... :$[/quote]
méthode abstraite?
Non, la méthode abstraite est le moyen de mettre le pattern en oeuvre, pas la pattern lui-même ...
je ne sais pas si c'est le pattern dont tu parles, mais ça lui ressemble beaucoup
http://www.exciton.cs.rice.edu/JavaResources/DesignPatterns/TemplatePattern.htm
Exactement !
je vous la donne ce soir, quand j'aurais mon bouquin sous la main ! 
Moi, je connaissais la traduction française du nom ...
ok je comprend mieux
merci pour les infos 
Wala, en français, ce pattern se nomme "Patron de méthode" (Template method) ...
sur le site www.dofactory.com on trouve l'ensemble des Desgin patterns, à l'adresse :
http://www.dofactory.com/patterns/patterns.aspx
bonne lecture...
j'ai du mal faire ! désolé !!!
faire un tour dans le code source du .NET framework ca peut pas mal aider pour concevoir ce genre de fonctionnalité
Shared Source Common Language Infrastructure 1.0 Release
http://www.microsoft.com/downloads/details.aspx?FamilyId=3A1C93FA-7462-47D0-8E56-8DD34C6292F0&displaylang=en
pour ce qui des copie, il faut pas oublier non plus comment se comporte ECMAscript,
toutes les primitives sont automatiquement copiée en "deep", cad la valeur est copiée et il n'y a plus de lien vers la reference d'origine, mais des qu'on copie autre chose qu'une primitive on a d'office un lien de reference.
si on prends le cas de la class Array,
faire un clone ce serait copier tous les elements de l'array (a 1 dimension) sans se soucier de leur type, et le fonctionnement interne de ECMAscript fait le boulot a notre place.
Par contre si on veut faire une "deep copy" d'une Array, là il faut forcer la copie des elements qui ne sont pas des primitives, car le but est d'avoir une copie complete de la valeur de l'element et de perdre le lien vers sa reference.
(utile quand on veut travailler sur un objet et faire des modifs sans modifier la source d'origine)
Et amha pour bien faire fonctionner tout ca, il faut ajouter des methodes comme copy et/ou clone aux core object, car si en .NET on a des methodes copy ou memberwiseClone pour la class Object, en ECMAscript on les a pas.
D'ou pourquoi mes ralages a repetition contre AS2, en ECMascript on peut modifier facilement les core object comme Object, Array, String etc...
sauf que y a plein de gens qui nous sortent que ah bah nan il ne faut pas modifier les classs AS2 par defaut, ah nan faut surtout pas y toucher et que si on le fait on a rien a compris a l'OO, car la bonne maniere de faire c'est de faire de l'heritage ou de la composition etc...
Dans un cas comme la copie d'object, c'est tres dur d'avoir une architecture propre et facile d'utilisation si justement on ne modifie pas ces core Object ECMAscript.
Oui c'est bien beau d'avoir une interface ICloneable, mais si elle n'est pas implémentée dans les objets par defaut ca ne sert pas a grand chose.
exemple:
Array.prototype.clone = function()et{
return this.concat();
}
Array.prototype.copy = function(){
var arr, i;
arr = new Array();
for( i=0; i<this.length; i++ )
{
arr[i] = this[i].copy();
}
return arr;
}
this[i].copy() permet de justement ne pas s'occuper du type que l'on veut copier,
mais pour que ca marche il faut que au moins tous les core object aient une methode copy defini dans leur class par defaut.
Oui, pour les matrices, je profite du fait qu'elles ne contiennent que des nombres pour considérer que le fait de dupliquer le tableau produit un clone ...
Pour la classe Grid, c'est plus tendencieux déjà ! 
Une autre manière de faire pourrait consister à créer une classe avec une méthode statique qui puisse cloner un objet ... Cette méthode manipulerait les prototypes, donc il s'agirait d'un hack pour AS2, mais éviterait de toucher aux objets natifs ...
Evidemment, une méthode clone() pour les objets natifs serait vraiment idéale !
Ca fait d'ailleurs partie des améliorations à mon avis indispensables pour la prochaine version de Flash ... :o
Merci pour le complément d'infos zwetan !
Fil des commentaires de ce billet