Académique Documents
Professionnel Documents
Culture Documents
Chapitres traités
Notion et intérêt des interfaces
Objets distribués
Dans l'exemple ci-dessus, il existe une classe FichePersonne. On désire qu'elle ne soit pas directement
accessible par les applications n°1 et n°2. La seule tolérance admise pour ces deux applications est de permettre
la visualisation de la fiche de chacune des personnes. On utilise donc un représentant de la classe qui est
l'interface Visualisation et qui valide la seule méthode affiche pour ce cas particulier.
Il existe une technologie récente qui est à la fois performante et séduisante qui permet de construire des objets
comme nous avons l'habitude de faire, mais qui ont la particularité d'être accessibles depuis n'importe quel
ordinateur du réseau. C'est la technologie des objets distribués. Elle est intéressante puisqu'elle permet de
construire les objets une fois pour toute sur une machine, et ainsi, il n'est pas nécessaire de les recopier sur tous
les autres ordinateurs du réseau. Attention toutefois, ces objets restent sur l'ordinateur où ils ont été créés. Ce
sont des objets distants qui fonctionnent en permanence, et donc la seule possibilité de les solliciter est de
passer par des requêtes sur le réseau, et donc de passer par des interfaces qui utilisent les méthodes
appropriées. Dans l'exemple ci-dessus, l'application du client désire récupérer l'adresse d'une personne, elle
passe donc par le représentant de la classe FichePersonne qui est l'interface RequêtePersonne qui dispose de la
méthode getAdresse (généralement, il existe plusieurs méthodes pour ce genre d'interface). On retrouve ici le
même principe que pour les bases de données.
Hiérarchies différentes
Il arrive assez souvent qu'on désire mettre en relation des objets qui n'ont apparemment aucun rapport entre
eux. C'est notamment le cas lorsqu'on désire afficher des objets qui sont issus de hiérarchies différentes. Il suffit
alors de prévoir une interface qui proposera une méthode commune à l'ensemble des objets à afficher. Dans
l'exemple ci-dessus, les classes Cercle et Carré héritent de la classe Forme qui dispose de la méthode Dessine.
Par ailleurs, la classe Texte qui est totalement indépendante de la première hiérarchie dispose également de la
méthode Dessine. Pour être sûr de dessiner un objet quelconque sur une fenêtre, par exemple, il suffit de passer
par l'interface Présentation qui dispose de la méthode Dessine et qui spécifie donc que les objets associés à
cette interface vont obligatoirement implémenter cette méthode.
Gestion événementielle
Tout système d'exploitation qui supporte des interfaces graphiques doit constamment surveiller l'environnement
afin de détecter des événements tels que la pression sur une touche du clavier ou sur un bouton de la souris.
Java contrôle complètement la manière dont les événements sont transmis de la source d'événement (par
exemple, un bouton ou un élément de menu) à l'écouteur d'événement (event listener). Vous pouvez désigner
n'importe quel objet comme écouteur d'événement. Lorsqu'un événement arrive à la source, celle-ci envoie une
notification à tous les objets écouteurs d'événements.
Comme il est possible d'avoir n'importe quel objet (issu d'une hiérarchie quelconque) comme écouteur
d'événement, il est nécessaire de passer par le système d'interface. En fait, un objet écouteur est une instance
d'une classe qui implémente l'interface spéciale appelée interface écouteur (listener interface). Dans l'exemple
ci-dessus, deux écouteurs ont été mis en oeuvre, l'objet relatif à un élément du menu et l'objet boutonCercle
(dans ce cas de figure, ils sont à la fois source et écouteur). Ils sont représentés par l'interface ActionListener,
et lorsqu'un un événement se produit (correspondant à un clic sur un bouton de la souris ou la validation par le
clavier) la méthode actionPerformed est exécutée.
Néanmoins, il n'est pas nécessaire de mentionner les mots clés public et abstract (nous pouvons quand même le
faire).
Une interface peut être dotée des mêmes droits d'accès qu'une classe (public ou droit de paquetage). Dans le
cas où nous désirons avoir une interface publique :
Cette interface particulière possède une méthode. Certaines interfaces possèdent plusieurs méthodes. Les
interfaces ne disposent jamais d'attributs (à part les constantes), et les méthodes ne sont jamais implémentées
directement dans l'interface. La fourniture des attributs et des implémentations de méthodes sont pris en charge
par la ou les classes qui implémentent l'interface.
Pour déclarer qu'une classe implémente une interface, employez le mot réservé implements :
Alors, la classe A :
1. dispose des constantes définies dans l'interface I.
2. est contrainte de définir toutes les méthodes prototypées dans l'interface I ; plus exactement, si la classe A ne
définit pas toutes les méthodes prototypées dans I, elle doit être déclarée abstraite et ne pourra donc pas être
instanciée.
Utilisation de l'interface
Sachant que la classe A implémente l'interface I, nous savons (si A n'est pas abstraite) qu'elle dispose de toutes
les méthodes de cette interface I ; nous possedons donc un renseignement sur ce dont nous pouvons faire avec
les instances de cette classe. Concrètement, en reprenant l'exemple de la classe FichePersonne et en utilisant
l'interface Visualisation, nous pouvons écrire :
On peut donc définir des variables de type interface, ici de type Visualisation ; nous pouvons alors invoquer
uniquement la méthode déclarée dans Visualisation sur un objet référencé par une variable de type Visualisation
ou, si cela avait été le cas, utiliser les constantes définies dans l'interface. Cela a des conséquences
fondamentales sur les possibilités de programmation.
1. Les attributs (constants) et les méthodes d'une interface sont automatiquement publics ; cela implique qu'une
classe qui implémente une interface devra déclarer publiques (avec le modificateur public) les méthodes de
l'interface qu'elle définit.
2. Si une classe A implémente une interface I, une sous-classe B de A implémente aussi automatiquement I ; une
instance B pourra être référencée par une variable de type I et bénéficiera, au moins par héritage, des
définitions des méthodes prototypées dans I.
Dans ce cas d'utilisation, les interfaces permettent de compenser, en grande partie, l'absence d'héritage
multiple.
Cependant, bien que vous ne puissez pas construire des objets interface, vous pouvez toujours déclarer des
variables interface :
Visualisation personne ;
Une variable interface doit faire référence à un objet d'une classe qui implémente l'interface :
La variable interface ne peut ensuite utiliser que les méthodes prévues par le contrat, c'est-à-dire, déclarées
dans l'interface :
personne.Affiche();
Bien entendu, on ne pourra pas affecter à i une référence à quelque chose de type I puisqu'on ne peut pas
instancier une interface (pas plus qu'on ne pouvait instancier une classe abstraite !). En revanche, on pourra
affecter à i n'importe quelle référence à un objet d'une classe implémentant l'interface I :
De plus, à travers i, on pourra manipuler des objets de classes quelconques, non nécessairement liées par
héritage, pour peu que ces classes implémentent l'interface I.
Voici un exemple illustrant cet aspect. Une interface Affichable comporte une méthode affiche. Deux classes
Entier et Flottant implémentent cette interface (aucun lien d'héritage n'apparaît ici). On crée un tableau
hétérogène de références de type Affichable qu'on remplit en instanciant des objets de type Entier et Flottant.
Résultat :
Cet exemple est restrictif puisqu'il peut se traiter avec une classe abstraite. Voyons maintenant ce que
l'interface apporte de plus.
Ces constantes sont automatiquement considérées comme si elles avaient été déclarées static et final. Il doit
s'agir obligatoirement d'expressions constantes. Elles sont accessibles en dehors d'une classe implémentant
l'interface. Par exemple, la constante MAXI de l'interface I se notera simplement I.MAXI.
Exemple de synthèse
Je vous propose un petit exemple de synthèse qui permet de stocker dans un même fichier des objets de classes
totalement différentes.
Principal.java
import java.io.*;
//------------------------------------------------------------------------------
interface Enregistrement extends Serializable {
void sauvegarder(ObjectOutputStream fichier);
void afficher();
}
//------------------------------------------------------------------------------
class Personne implements Enregistrement {
private String nom, prénom;
Principal.java
Cet exercice consiste à créer une classe Cercle qui sera capable
entre autre d'afficher un cercle de diamètre 100 aux coordonnées
fixées au moment de la création ainsi qu'une classe Carré qui
sera également capable d'afficher un carré de 100 pixels de côté.
Ces deux classes hériterons d'une classe Forme qui possède les
attributs correspondant aux coordonnées centrale de chaque type
de figure ainsi qu'une méthode d'affichage vide. Vous allez
rajouter une classe que vous nommerez Texte qui aura la
particularité de stocker une chaîne de caractères de votre choix à
une position bien déterminée.
Lorsque vous proposerez l'affichage, il faudra lancer l'affichage d'un élément sans le connaître au préalable.
Etant donné que la classe Texte n'a absolument rien à voir avec toute la hiérarchie de la classe Forme, il est
nécessaire de passer par une interface que vous appellerez Présentation, et la connexion se fera par la méthode
dessine. Le cas échéant, faites toutes les modifications qui vous semblent nécessaires pour avoir une écriture
cohérente.