Documentation Flash CS3 |
|||
| Programmation avec ActionScript 3.0 > Programmation orientée objet en ActionScript > Héritage | |||
L'héritage est une forme de réutilisation du code qui permet aux programmeurs de développer de nouvelles classes à partir de classes existantes. Les classes existantes sont alors fréquemment appelées classes de base ou super-classes, alors que les nouvelles classes sont généralement appelées sous-classes. L'un des principaux avantages de l'héritage est qu'il permet de réutiliser le code d'une classe de base sans modifier le code existant. De plus, l'héritage ne nécessite pas de modifier les modes d'interaction des autres classes avec la classe de base. Plutôt que de modifier une classe existante, qui est peut-être soigneusement testée et déjà utilisée, l'héritage permet de traiter cette classe comme un module intégré qui peut être étendu à l'aide de propriétés et de méthodes supplémentaires. C'est pourquoi on utilise le mot-clé extends pour indiquer qu'une classe hérite d'une autre classe.
L'héritage permet également de tirer parti du polymorphisme du code. Le polymorphisme est la possibilité d'utiliser un nom de méthode unique pour une méthode qui se comporte différemment en fonction des types de données qu'elle reçoit. Par exemple, supposons une classe de base nommée Shape, avec deux sous-classes nommées Circle et Square. La classe Shape définit une méthode nommée area(), qui renvoie la surface de Shape. Si vous avez implémenté le polymorphisme, vous pouvez appeler la méthode area() pour les objets de type Circle ou Square et lui faire exécuter le calcul correct. L'héritage autorise le polymorphisme en permettant aux sous-classes d'hériter et redéfinir, ou override les méthodes de la classe de base. Dans l'exemple suivant, la méthode area() est redéfinie par les classes Circle et Square :
class Shape
{
public function area():Number
{
return NaN;
}
}
class Circle extends Shape
{
private var radius:Number = 1;
override public function area():Number
{
return (Math.PI * (radius * radius));
}
}
class Square extends Shape
{
private var side:Number = 1;
override public function area():Number
{
return (side * side);
}
}
var cir:Circle = new Circle();
trace(cir.area()); // résultat : 3.141592653589793
var sq:Square = new Square();
trace(sq.area()); // résultat : 1
Dans la mesure où chaque classe définit un type de données, l'utilisation de l'héritage crée une relation spéciale entre une classe de base et une classe qui l'étend. Une sous-classe possède obligatoirement toutes les propriétés de sa classe de base, ce qui signifie qu'il est toujours possible de substituer une instance d'une sous-classe à une instance de la classe de base. Par exemple, si une méthode définit un paramètre du type Shape, il est parfaitement autorisé de lui passer un argument du type Circle, car Circle étend Shape, comme on le voit ci-dessous :
function draw(shapeToDraw:Shape) {}
var myCircle:Circle = new Circle();
draw(myCircle);
Qu'elle soit définie à l'aide du mot-clé function, var ou const, une propriété d'une instance est héritée par toutes les sous-classes tant que cette propriété n'est pas déclarée avec l'attribut private dans la classe de base. Par exemple, la classe Event de l'API de Flash Player possède de nombreuses sous-classes qui héritent de propriétés communes à tous les objets événements.
Pour certains types d'événements, la classe Event contient toutes les propriétés nécessaires pour définir l'événement. Ces types d'événements ne nécessitent pas de propriétés d'instance au-delà de celles qui sont définies dans la classe Event. L'événement complete, qui est déclenché lorsque des données ont été chargées avec succès, et l'événement connect, qui se produit lorsqu'une connexion réseau a été établie, sont des exemples de ce type d'événement.
L'exemple suivant est extrait de la classe Event. Il montre certaines propriétés et méthodes dont les sous-classes héritent. Étant héritées, ces propriétés sont accessibles par une instance de n'importe quelle sous-classe.
public class Event
{
public function get type():String;
public function get bubbles():Boolean;
...
public function stopPropagation():void {}
public function stopImmediatePropagation():void {}
public function preventDefault():void {}
public function isDefaultPrevented():Boolean {}
...
}
D'autres types d'événements nécessitent des propriétés uniques qui ne sont pas disponibles dans la classe Event. Ces événements sont définis à l'aide de sous-classes de la classe Event, ce qui permet d'ajouter de nouvelles propriétés à celles de cette classe Event. La classe MouseEvent est un exemple de sous-classe de ce type. Elle ajoute des propriétés uniques aux événements associés à un mouvement ou à un clic de souris, tels que les événements mouseMove et click. L'exemple suivant est extrait de la classe MouseEvent. Il montre la définition des propriétés inhérente à la sous-classe parce qu'absentes de la classe de base.
public class MouseEvent extends Event
{
public static const CLICK:String = "click";
public static const MOUSE_MOVE:String = "mouseMove";
...
public function get stageX():Number {}
public function get stageY():Number {}
...
}
Si une propriété est déclarée avec le mot-clé public, elle est visible de n'importe quel point du code. Cela signifie que contrairement aux mots-clés private, protected et internal, le mot-clé public n'introduit aucune restriction sur l'héritage des propriétés.
Si une propriété est déclarée avec le mot-clé private, elle n'est visible qu'à partir de la classe qui la définit, autrement dit les sous-classes n'en héritent pas. Ce comportement est différent de celui des versions antérieures d'ActionScript, où le mot-clé private se comportait plutôt comme le mot-clé protected en ActionScript 3.0.
Le mot-clé protected indique qu'une propriété est visible non seulement à partir de la classe qui la définit, mais aussi à partir de toutes les sous-classes de celle-ci. Contrairement au mot-clé protected en Java, en ActionScript 3.0 le mot-clé protected ne rend pas une propriété visible à partir de toutes les autres classes du même paquet. En ActionScript 3.0, seules les sous-classes peuvent accéder à une propriété déclarée avec le mot-clé protected. De plus, une propriété protégée est visible à partir d'une sous-classe même si celle-ci ne se trouve pas dans le même paquet que sa classe de base.
Pour limiter la visibilité d'une propriété au paquet dans lequel elle est définie, vous pouvez soit utiliser le mot-clé internal, soit n'utiliser aucun spécificateur de contrôle d'accès. Le spécificateur de contrôle d'accès internal est appliqué par défaut si vous n'en indiquez aucun. Seule une sous-classe résidant dans le même paquet pourra hériter d'une propriété marquée comme internal.
L'exemple suivant montre comment chaque spécificateur de contrôle d'accès affecte l'héritage dans et au-delà des paquets. Le code ci-dessous définit une classe principale d'application nommée AccessControl et deux autres classes, Base et Extender. La classe Base se trouve dans un paquet nommé foo et la classe Extender, qui est une sous-classe de la classe Base, dans un paquet nommé bar. La classe AccessControl n'importe que la classe Extender et crée une instance de celle-ci qui tente d'accéder à une variable nommée str, définie dans la classe Base. La variable str est déclarée comme public, si bien que le code est compilé et exécuté comme ci-dessous :
// Base.as dans un dossier nommé foo
package foo
{
public class Base
{
public var str:String = "hello"; // cette ligne change public
}
}
// Extender.as dans un dossier nommé bar
package bar
{
import foo.Base;
public class Extender extends Base
{
public function getString():String {
return str;
}
}
}
// classe principale de l'application dans le fichier ProtectedExample.as
import flash.display.MovieClip;
import bar.Extender;
public class AccessControl extends MovieClip
{
public function AccessControl()
{
var myExt:Extender = new Extender();
trace(myExt.testString); // erreur si str n'est pas publique
trace(myExt.getString()); // erreur si str est privée ou interne
}
}
}
Pour voir l'effet des autres spécificateurs de contrôle d'accès lors de la compilation et de l'exécution de cet exemple, changez le spécificateur de contrôle d'accès de str en private, protected ou internal après avoir supprimé ou mis en commentaire la ligne suivante dans la classe AccessControl :
trace(myExt.testString); // erreur si str n'est pas publique
Les propriétés déclarées à l'aide des mots-clés var ou const sont héritées mais ne peuvent pas être redéfinies. Redéfinir, ou forcer, une propriété au sens de « override » signifie redéfinir cette propriété dans une sous-classe. Les méthodes (c'est à dire les propriétés déclarées avec le mot-clé function) sont le seul type de propriété qu'il est possible de redéfinir. Bien qu'il soit impossible de redéfinir une variable d'instance, vous pouvez obtenir le même résultat en créant des méthodes de lecture et de définition pour cette variable d'instance et en forçant ces méthodes. Pour plus d'informations, consultez la section Redéfinition des méthodes de lecture et de définition.
Redéfinir, ou forcer, une méthode au sens de « override » signifie redéfinir le comportement d'une méthode héritée. Les méthodes statiques ne sont pas héritées et ne peuvent donc pas être redéfinies. Toutefois, les sous-classes héritent des méthodes d'instance et il est donc possible de redéfinir celles-ci sous réserve de deux conditions :
final dans la classe de base. Lorsqu'il est utilisé avec une méthode d'instance, le mot-clé final indique que le programmeur veut empêcher les sous-classes de redéfinir cette méthode.private dans la classe de base. Si une méthode est marquée comme private dans la classe de base, il n'est pas nécessaire d'utiliser le mot-clé override lors de la définition d'une méthode portant le même nom dans la sous-classe, puisque la méthode de la classe de base n'est pas visible à partir de la sous-classe.Pour redéfinir une méthode d'instance correspondant à ces critères, la définition de la méthode dans la sous-classe doit utiliser le mot-clé override et correspondre comme défini ci-dessous à la version de cette méthode dans la super-classe :
Toutefois, il n'est pas nécessaire que les noms des paramètres de la méthode de redéfinition correspondent à ceux des paramètres de la classe de base, tant que le nombre de paramètres et leurs types de données correspondent.
Il arrive fréquemment que les programmeurs souhaitent compléter le comportement de la méthode de super-classe qu'ils redéfinissent, plutôt que remplacer ce comportement. Il est donc nécessaire de disposer d'un mécanisme permettant à une méthode d'une sous-classe d'appeler sa « méthode mère » dans la super-classe. Ce mécanisme est assuré par l'instruction super, qui contient une référence à la super-classe immédiate. L'exemple suivant définit une classe nommée Base, qui contient la méthode thanks(), et une sous-classe de Base nommée Extender, qui redéfinit la méthode thanks() . La méthode Extender.thanks() utilise l'instruction super pour appeler Base.thanks().
package {
import flash.display.MovieClip;
public class SuperExample extends MovieClip
{
public function SuperExample()
{
var myExt:Extender = new Extender()
trace(myExt.thanks()); // résultat : Mahalo nui loa
}
}
}
class Base {
public function thanks():String
{
return "Mahalo";
}
}
class Extender extends Base
{
override public function thanks():String
{
return super.thanks() + " nui loa";
}
}
Bien qu'il soit impossible de redéfinir les variables définies dans une super-classe, il est possible de redéfinir les méthodes de lecture et de définition. Par exemple, le code suivant redéfinit une méthode de lecture appelée currentLabel, définie dans la classe MovieClip de l'API de Flash Player.
package
{
import flash.display.MovieClip;
public class OverrideExample extends MovieClip
{
public function OverrideExample()
{
trace(currentLabel)
}
override public function get currentLabel():String
{
var str:String = "Override: ";
str += super.currentLabel;
return str;
}
}
}
Le résultat de l'instruction trace() dans le constructeur de la classe OverrideExample est Override: null, ce qui montre que cet exemple a réussi à redéfinir la propriété héritée currentLabel.
Les sous-classes n'héritent pas des propriétés statiques. Ces dernières ne sont donc pas accessibles via une instance d'une sous-classe. Une propriété statique n'est accessible que par le biais de l'objet classe dans lequel elle est définie. Par exemple, le code suivant définit une classe de base nommée Base et une sous-classe nommée Extender, qui étend la classe Base. Une variable statique nommée test est définie dans la classe Base. Le code de l'extrait ci-dessous ne peut être compilé en mode strict et génère une erreur d'exécution en mode standard.
package {
import flash.display.MovieClip;
public class StaticExample extends MovieClip
{
public function StaticExample()
{
var myExt:Extender = new Extender();
trace(myExt.test); // Erreur
}
}
}
class Base {
public static var test:String = "static";
}
class Extender extends Base { }
La seule façon d'accéder à la variable statique test est via l'objet classe, comme le montre le code suivant :
Base.test;
Il est toutefois permis de définir une propriété d'instance ayant le même nom qu'une propriété statique. Cette propriété d'instance peut être définie dans la même classe que la propriété statique, ou dans une sous-classe. Par exemple, la classe Base de l'exemple précédent peut comporter une propriété d'instance nommée test. Le code suivant peut être compilé et exécuté normalement, car la classe Extender hérite de la propriété d'instance. Ce code peut aussi être compilé et exécuté normalement si la définition de la variable d'instance test est déplacée (mais non copiée) dans la classe Extender.
package
{
import flash.display.MovieClip;
public class StaticExample extends MovieClip
{
public function StaticExample()
{
var myExt:Extender = new Extender();
trace(myExt.test); // résultat : instance
}
}
}
class Base
{
public static var test:String = "static";
public var test:String = "instance";
}
class Extender extends Base {}
Bien que les propriétés statiques ne soient pas héritées, elles figurent dans la chaîne de portée de la classe qui les définit et de toute sous-classe de celle-ci. C'est pourquoi on dit que les propriétés statiques sont dans la portée de la classe dans laquelle elles sont définies et des sous-classes de celle-ci. Cela signifie qu'une propriété statique est directement accessible à partir du corps de la classe qui la définit et de toute sous-classe de celle-ci.
L'exemple suivant modifie les classes définies dans l'exemple précédent pour montrer que la variable statique test, définie dans la classe Base, est dans la portée de la classe Extender. Autrement dit, la classe Extender peut accéder à la variable statique test sans qu'il soit nécessaire de faire précéder le nom de celle-ci du nom de la classe qui définit test.
package
{
import flash.display.MovieClip;
public class StaticExample extends MovieClip
{
public function StaticExample()
{
var myExt:Extender = new Extender();
}
}
}
class Base {
public static var test:String = "static";
}
class Extender extends Base
{
public function Extender()
{
trace(test); // résultat : static
}
}
Si une propriété d'instance est définie avec le même nom qu'une propriété statique de la même classe ou d'une super-classe, la propriété d'instance est prioritaire dans la chaîne de portée. On dit alors que la propriété d'instance fait de l'ombre à la propriété statique, ce qui signifie que la valeur de la propriété d'instance est utilisée à la place de celle de la propriété statique. Par exemple, le code suivant montre que si la classe Extender définit une variable d'instance nommée test, l'instruction trace() utilise la valeur de la variable d'instance au lieu de celle de la variable statique.
package
{
import flash.display.MovieClip;
public class StaticExample extends MovieClip
{
public function StaticExample()
{
var myExt:Extender = new Extender();
}
}
}
class Base
{
public static var test:String = "static";
}
class Extender extends Base
{
public var test:String = "instance";
public function Extender()
{
trace(test); // résultat : instance
}
}
Flash CS3
M'envoyer un message électronique lorsque des commentaires sont ajoutés à cette page | Rapport de commentaire
Page en cours: http://livedocs.adobe.com/flash/9.0_fr/main/00000068.html