US

Projet 6 : Enrichir les classes

Ce projet tourne autour de l'écriture de classes et d'interface en utilisant des notions plus avancées. Comment écrire une interface, à quoi ça sert ? Comment utiliser les variables de classe, la référence this, ... ? Que sont les types énumérés ? Ce projet pratique se concentre précisément autour de toutes ces questions.

Le problème

On va définir toute une série de classe qui vont représenter des formes 2D qui seront placées dans le plan. On pourrait éventuellement utiliser ces classes dans le cadre d'un programme de dessin. Étant donné qu'il existe plusieurs formes mais pour lesquelles on aimerait disposer des mêmes méthodes, on va utiliser une interface pour rassembler tout ça.

Définition d'une interface

Comme d'habitude, créez un nouveau projet dans Eclipse, on ne nommera Projet6. On va maintenant ajouter une interface à notre projet, cette interface représentera une forme. Faites donc un clic droit sur le projet et puis choisissez le menu New > Interface.

Créer une nouvelle interface
fig j1 Créer une nouvelle interface

Donnez comme nom à l'interface Shape puisqu'elle va représenter une forme. Il s'agit maintenant de la compléter. Pour rappel, une interface contient des définitions de méthodes mais sans corps, c'est-à-dire une liste de signatures et également des définitions de variables. Nous allons donc ajouter quelques méthodes qui sont communes à toutes les formes.

1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
public interface Shape
{
    // * * * Les variables * * *
    public Vector2D ORIGIN = new Vector2D (0, 0); // Origine dans le plan
 
    // * * * Les méthodes * * *
    public Vector2D getLocation(); // Position de la forme dans le plan
    public double getPerimeter(); // Périmètre de la forme
    public double getArea(); // Surface de la forme
}
listing j1 Interface Shape

On a définit trois méthodes dans l'interface. La première permet d'obtenir la position de la forme dans le plan. Cette position est contenue dans un objet Vector2D (voir projet 5). Ensuite, les deux dernières méthodes permettent d'obtenir le périmètre et la surface de la forme.

On ajoute enfin une variable dans l'interface. Cette variable est par défaut une constance (final), c'est-à-dire qu'on ne peut modifier sa valeur, et une variable de classe (static), c'est-à-dire qu'il n'y a qu'une copie de la variable qui est attachée à l'interface.

L'interface Shape
fig j2 L'interface Shape

Implémenter l'interface

Implémenter une interface, c'est fournir une implémentation pour toutes les méthodes définies dans l'interface. On va donc écrire une classe qui représente un carré et cette classe va implémenter l'interface Shape. On peut dire qu'un carré est une forme.

Pour rappel, on utilise le mot réservé implements pour signaler qu'une classe implémente une interface. Une fois encore Eclipse va bien nous aider. Créons notre classe par un clic droit sur le projet puis en choisissant le menu New > Class.

Créer la classe Square
fig j3 Créer la classe Square

Dans la fenêtre qui vous est désormais familière, encodez le nom de la classe : Square et ensuite, cliquez sur le bouton Add à droite de la zone de texte Interface. Dans la nouvelle fenêtre qui s'ouvre, retrouvez notre interface Shape dans la liste après avoir commencé à taper son nom, sélectionnez-là, fermez la fenêtre et créez la classe Square.

Implémenter une interface
fig j4 Implémenter une interface

Eclipse va vous créer la classe Square et déjà la remplir pour vous. En effet, cette classe doit implémenter toutes les méthodes définies dans l'interface Shape.

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 
public class Square implements Shape
{
    /* (non-Javadoc)
     * @see Shape#getArea()
     */
    public double getArea()
    {
        // TODO Auto-generated method stub
        return 0;
    }
 
    /* (non-Javadoc)
     * @see Shape#getLocation()
     */
    public Vector2D getLocation()
    {
        // TODO Auto-generated method stub
        return null;
    }
 
    /* (non-Javadoc)
     * @see Shape#getPerimeter()
     */
    public double getPerimeter()
    {
        // TODO Auto-generated method stub
        return 0;
    }
}
listing j2 Classe Square par Eclipse

Les variables d'instance

Lorsqu'on écrit une nouvelle classe, on commence par lui ajouter ses variables d'instance. Ici, pour un carré, on va stocker la longueur de son côté et sa position dans le plan. Il nous faut donc deux variables d'instance.

1 
2 
private double side; // Longueur du côté
private Vector2D location; // Position dans le plan
listing j3 Variables d'instance de la classe Square

Le constructeur

Ensuite, on définit un ou plusieurs constructeurs qui vont permettre de créer des instances de la classe. Notre constructeur prend deux paramètres : la longueur du côté et la position dans le plan.

1 
2 
3 
4 
5 
public Square (double side, Vector2D location)
{
    this.side = side;
    this.location = location;
}
listing j4 Constructeur de la classe Square

Que fait le constructeur ? Il se contente de copier la valeur des paramètres qu'il reçoit dans les variables d'instance de la classe. Maintenant, remarquez que les paramètres portent le même nom que les variables d'instance et il nous faut donc utiliser le mot réservé this pour différencier les deux variables. Pour rappel, this se réfère à l'instance elle-même.

Ajoutons maintenant un autre constructeur à la classe. Ce constructeur prend un seul paramètre qui est la longueur du côté et permet de créer un carré de la longueur spécifiée et qui est à la position (0, 0). On va à nouveau utiliser la référence this pour appeler un constructeur de notre propre classe. Rappelez-vous qu'un appel à un autre constructeur doit toujours être la première instruction.

1 
2 
3 
4 
public Square (double side)
{
    this (side, Shape.ORIGIN);
}
listing j5 Constructeur de la classe Square (2)

Le constructeur qu'on appelle avec this prend deux paramètres. Le premier est la longueur du côté et on lui passe simplement le paramètre side qu'on a reçu et le second est la position dans le plan. On doit passer la position (0,0). On peut bien sûr passer new Vector2D (0, 0).

Mais rappelez-vous qu'on avait une variable dans l'interface Shape qui représentait l'origine du plan. On va donc accéder à cette variable en utilisant le nom de l'interface suivi de l'opérateur point suivi du nom de la variable puisque cette variable est une variable statique.

Les méthodes

On s'occupe maintenant des méthodes de la classe. On doit tout d'abord obligatoirement implémenter les méthodes présentes dans l'interface.

1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
/* (non-Javadoc)
* @see Shape#getArea()
*/
public double getArea()
{
    return side * side;
}
 
/* (non-Javadoc)
* @see Shape#getLocation()
*/
public Vector2D getLocation()
{
    return location;
}
 
/* (non-Javadoc)
* @see Shape#getPerimeter()
*/
public double getPerimeter()
{
    return 4 * side;
}
listing j6 Implémentation des méthodes de l'interface Shape dans la classe Square

La surface d'un carré se calcule en faisant côté * côté, pour le périmètre on fait 4 * côté et pour la position dans le plan, on renvoie simplement la variable d'instance location.

Test

Une autre classe Circle est également créée et représente un cercle. Pour le calcul du périmètre et de la surface, on doit utiliser le nombre π que l'on peut obtenir via la constante statique PI qui se trouve dans la classe Math. Essayez donc d'écrire cette classe par vous-mêmes pour vous entrainer.

Testons maintenant tout cela mais avant tout. Créez donc une nouvelle classe Projet6 avec une méthode main.

1 
2 
3 
4 
5 
6 
7 
public static void main (String[] args)
{
    Shape carre = new Square (10);
    out.println ("Carré de 10 de côté :");
    out.println ("\tPérimètre: " + carre.getPerimeter());
    out.println ("\tSurface: " + carre.getArea());
}
listing j7 Test

On crée une variable carre de type Shape. Cette variable pourra donc contenir une référence vers n'importe quelle instance d'une classe qui implémente l'interface Shape et on ne pourra invoquer que les méthodes définies dans l'interface Shape.

On affecte à la variable une nouvelle instance de la classe Square représentant un carré de 10 unités de côté et on affiche à la console la valeur du périmètre et de la surface du carré. L'exécution du programme affiche à la console :

Carré de 10 de côté :
	Périmètre: 40.0
	Surface: 100.0

Types énumérés

Voyons maintenant un dernier point à savoir les types énumérés. On a souvent besoin d'utiliser des formes canoniques, c'est-à-dire des formes dont les dimensions sont l'unité, par exemple un carré d'une unité de côté, un cercle d'une unité de rayon, ...

Afin d'éviter des créer dix milles fois les mêmes instances, on va créer une instance de chaque forme canonique et les stocker dans un type énuméré. On pourra ainsi les utiliser quand on veut sans à chaque fois recréer un nouvel objet.

Faites un clic droit sur le projet et cliquez sur le menu New > Enum. On crée une nouvelle énumération dont le nom est CanonicalShapes.

Créer le type énuméré CanonicalShapes
fig j5 Créer le type énuméré CanonicalShapes

La première chose à faire est de définir les valeurs possibles de l'énumération. On va se contenter d'un carré et d'un cercle canonique.

1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
public enum CanonicalShapes
{
    // Valeurs de l'énumération
    SQUARE (new Square (1)),
    CIRCLE (new Circle (1));
 
    // Variables d'instance
    private Shape shape;
 
    // Constructeur
    private CanonicalShapes (Shape shape)
    {
        this.shape = shape;
    }
}
listing j8 Valeurs de l'énumération

Les deux valeurs de l'énumération sont bien définies et grâce au constructeur, on stocke les instances des formes dans la variable d'instance de l'énumération. Maintenant, on souhaite que l'énumération soit une forme et il faut donc implémenter l'interface Shape. L'implémentation des méthodes de l'interface est assez facile, on délègue à la variable d'instance shape.

1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
public enum CanonicalShapes implements Shape
{
    // [...]
 
    public double getArea()
    {
        return shape.getArea();
    }
    public Vector2D getLocation()
    {
        return shape.getLocation ();
    }
    public double getPerimeter()
    {
        return shape.getPerimeter();
    }
}
listing j9 Implémentation de l'interface Shape

Test

Testons maintenant si nos formes canoniques sont bien implémentées.

1 
2 
3 
4 
5 
6 
7 
public static void main (String[] args)
{
    Shape cercle = CanonicalShapes.CIRCLE;
    out.println ("Cercle canonique :");
    out.println ("\tPérimètre: " + cercle.getPerimeter());
    out.println ("\tSurface: " + cercle.getArea());
}
listing j10 Test (2)

Le code fonctionne bien et affiche à la console :

Cercle canonique :
	Périmètre: 6.283185307179586
	Surface: 3.141592653589793

Les valeurs affichées correspondent bien au périmètre et à la surface d'un cercle avec un rayon d'une unité.

Pour aller plus loin

Pour aller plus loin, nous vous proposons d'ajouter quelques formes supplémentaires comme par exemple un rectangle, un parallélogramme, un trapèze régulier, ... et n'oubliez pas de chaque fois ajouter la forme canonique correspondante dans le type énuméré CanonicalShapes.

Une autre extension est d'ajouter une nouvelle méthode à l'interface Shape. Cette nouvelle méthode permet de modifier la position de la forme en effectuant une simple translation de dx selon l'axe des x et de dy selon l'axe des y. Sa signature est la suivante :

1 
2 
3 
4 
/*
* Effectue une translation de la forme
*/
public void translate (double dx, double dy);
listing j11 Méthode translate dans l'interface Shape

Résumé des points importants

  • Une interface contient des définitions de méthodes et des variables ;
  • Une classe qui implémente une interface doit fournir une implémentation pour toutes les méthodes définies dans l'interface ;
  • Une variable d'instance existe pour chaque instance de la classe et une variable de classe (ou statique) existe en une seule copie attachée à une classe (ou à une interface) ;
  • Le mot réservé this référence l'instance elle-même ;
  • Une variable dont le type est une interface peut contenir une référence vers toute instance d'une classe qui implémente l'interface ;
  • Avec une variable dont le type est une interface, on ne peut invoquer que des méthodes qui sont définies dans l'interface ;

Vous pouvez télécharger le projet si vous le désirez. Ensuite, vous pouvez poursuivre le tutoriel, faire les exercices ou passer au projet suivant.

Pour tout problème, toute remarque, rendez-vous sur le forum consacré aux exercices pratiques.

  • Espace membre
  • Learning Center
  • Les forums
  • Livre d'or
  • Imprimer
  • Boutique
  • Info
  • Règlement
  • Erreur
  • Newsletter

MyPagerank.Net

Firefox 3.6

Browse Happy logo

Open Clip Art Library

Join our Facebook Group

Twitter

Copyright © 2000-2018 UKO. Toute reproduction strictement interdite sans autorisation du webmaster

Valid XHTML 1.1 !
Valid CSS2 !
Level Triple-A conformance icon, W3C-WAI Web Content Accessibility Guidelines 1.0
ICRA Internet Content Rating Association
Creative Commons License
Site optimisé pour Firefox avec une résolution 1024x768 --- Page chargée en 0.0855539 secondes --- This site uses Thumbshots previews