CodeGym /Blog Java /Random-FR /Pourquoi avons-nous besoin d'interfaces en Java
Auteur
Aditi Nawghare
Software Engineer at Siemens

Pourquoi avons-nous besoin d'interfaces en Java

Publié dans le groupe Random-FR
Salut! Aujourd'hui nous allons parler d'un concept important en Java : les interfaces. Le mot vous est probablement familier. Par exemple, la plupart des programmes informatiques et des jeux ont des interfaces. Au sens large, une interface est une sorte de « télécommande » qui relie deux parties en interaction. Un exemple simple d'interface dans la vie de tous les jours est la télécommande d'un téléviseur. Il connecte deux objets - une personne et un téléviseur - et effectue différentes tâches : augmenter ou baisser le volume, changer de chaîne et allumer ou éteindre le téléviseur. Une partie (la personne) doit accéder à l'interface (appuyer sur un bouton de la télécommande) pour que la seconde partie effectue l'action. Par exemple, pour faire passer le téléviseur à la chaîne suivante. De plus, l'utilisateur ne Vous n'avez pas besoin de savoir comment le téléviseur est organisé ou comment le processus de changement de chaîne est mis en œuvre en interne. La seule chose à laquelle l'utilisateur a accès est l'interface. L'objectif principal est d'obtenir le résultat souhaité. Qu'est-ce que cela a à voir avec la programmation et Java ? Tout :) La création d'une interface est très similaire à la création d'une classe normale, mais à la place en utilisant le motclasse , nous indiquons le mot interface . Regardons l'interface Java la plus simple, voyons comment elle fonctionne et pourquoi nous en aurions besoin :

public interface CanSwim {

     public void swim();
}
Nous avons créé une interface CanSwim . C'est un peu comme notre télécommande, mais avec un 'bouton' : la méthode swim() . Mais comment utilise-t-on cette télécommande ? Pour ce faire, nous devons implémenter une méthode, c'est-à-dire notre bouton de télécommande. Pour utiliser une interface, certaines classes de notre programme doivent implémenter ses méthodes. Inventons une classe dont les objets « peuvent nager ». Par exemple, une classe Canard convient :

public class Duck implements CanSwim {

    public void swim() {
        System.out.println("Duck, swim!");
    }

    public static void main(String[] args) {

        Duck duck = new Duck();
        duck.swim();
    }
}
"Que voyons-nous ici ? La classe Canard est 'associée' à l' interface CanSwim par le mot clé implements . Pour plus de clarté, nous pouvons traduire ' la classe publique Duck implements CanSwim ' littéralement par : ' La classe publique Duck implémente l'interface CanSwim ". Cela signifie qu'une classe associée à une interface doit implémenter toutes ses méthodes. Remarque : notre Duckclasse, tout comme l' CanSwiminterface, a une swim()méthode, et elle contient une certaine logique. C'est une exigence obligatoire. Si nous écrivons simplementpublic class Duck implements CanSwimsans créer de swim()méthode dans la Duckclasse, le compilateur nous donnera une erreur : Duck n'est pas abstrait et ne remplace pas la méthode abstraite swim() dans CanSwim Pourquoi ? Pourquoi cela arrive-t-il? Si nous expliquons l'erreur à l'aide de l'exemple du téléviseur, cela reviendrait à donner à quelqu'un une télécommande de téléviseur avec un bouton "changer de chaîne" qui ne peut pas changer de chaîne. Vous pouvez appuyer sur le bouton autant que vous le souhaitez, mais cela ne fonctionnera pas. La télécommande ne change pas de chaîne par elle-même : elle envoie uniquement un signal au téléviseur, qui met en œuvre le processus complexe de changement de chaîne. Il en va de même pour notre canard : il doit savoir nager pour pouvoir être appelé via l' CanSwiminterface. S'il ne sait pas comment, leCanSwiml'interface ne relie pas les deux parties — la personne et le programme. La personne ne pourra pas utiliser la swim()méthode pour faire une Ducknage à l'intérieur du programme. Vous comprenez maintenant plus clairement à quoi servent les interfaces. Une interface décrit le comportement que les classes implémentant l'interface doivent avoir. 'Comportement' est un ensemble de méthodes. Si nous voulons créer plusieurs messagers, le plus simple est de créer une Messengerinterface. De quoi chaque messager a-t-il besoin ? Au niveau de base, ils doivent être capables de recevoir et d'envoyer des messages.

public interface Messenger{

     public void sendMessage();

     public void getMessage();
}
Maintenant, nous pouvons simplement créer nos classes de messagerie qui implémentent l'interface correspondante. Le compilateur lui-même nous "forcera" à les implémenter dans nos classes. Télégramme:

public class Telegram implements Messenger {

    public void sendMessage() {

        System.out.println("Sending a Telegram message!");
    }

     public void getMessage() {
         System.out.println("Receiving a Telegram message!");
     }
}
Whatsapp :

public class WhatsApp implements Messenger {

    public void sendMessage() {

        System.out.println("Sending a WhatsApp message!");
    }

     public void getMessage() {
         System.out.println("Reading a WhatsApp message!");
     }
}
Viber :

public class Viber implements Messenger {

    public void sendMessage() {

        System.out.println("Sending a Viber message!");
    }

     public void getMessage() {
         System.out.println("Receiving a Viber message!");
     }
}
Quels avantages cela procure-t-il ? Le plus important d'entre eux est le couplage lâche. Imaginez que nous concevons un programme qui collectera les données des clients. La Clientclasse a définitivement besoin d'un champ pour indiquer quel messager spécifique le client utilise. Sans interfaces, cela aurait l'air bizarre :

public class Client {

    private WhatsApp whatsApp;
    private Telegram telegram;
    private Viber viber;
}
Nous avons créé trois champs, mais un client ne peut avoir qu'un seul messager. Nous ne savons tout simplement pas lequel. Nous devons donc ajouter toutes les possibilités à la classe afin de pouvoir communiquer avec le client. Il s'avère qu'un ou deux d'entre eux seront toujours null, totalement inutiles au programme. Il est préférable d'utiliser notre interface à la place :

public class Client {

    private Messenger messenger;
}
Ceci est un exemple de couplage lâche ! Au lieu de spécifier une classe de messager spécifique dans la Clientclasse, nous indiquons simplement que le client a un messager. Lequel exactement sera déterminé pendant l'exécution du programme. Mais pourquoi avons-nous besoin d'interfaces pour cela ? Pourquoi ont-ils même été ajoutés à la langue? C'est une bonne question - et la bonne question! Ne pouvons-nous pas obtenir le même résultat en utilisant l'héritage ordinaire ? La Messengerclasse en tant que parent, et Viber, Telegram, et WhatsAppen tant qu'enfants. Effectivement, c'est possible. Mais il y a un hic. Comme vous le savez déjà, Java n'a pas d'héritage multiple. Mais il existe un support pour plusieurs interfaces. Une classe peut implémenter autant d'interfaces que vous le souhaitez. Imaginons que nous ayons une Smartphoneclasse qui a unAppchamp, qui représente une application installée sur le smartphone.

public class Smartphone {

    private App app;
}
Bien sûr, une application et un messager sont similaires, mais ce sont toujours des choses différentes. Il peut y avoir des versions mobiles et de bureau d'un messager, mais App représente spécifiquement une application mobile. Voici le problème — si nous utilisions l'héritage, nous ne serions pas en mesure d'ajouter un Telegramobjet à la Smartphoneclasse. Après tout, la Telegramclasse ne peut pas hériter simultanément Appde et Messenger! Et nous l'avons déjà fait hériter Messengeret l'avons ajouté à la Clientclasse. Mais la Telegramclasse peut facilement implémenter les deux interfaces ! En conséquence, nous pouvons donner à la Clientclasse un Telegramobjet en tant que Messenger, et nous pouvons le donner à la Smartphoneclasse en tant que App. Voici comment procéder :

public class Telegram implements Application, Messenger {

    // ...methods
}

public class Client {

    private Messenger messenger;

    public Client() {
        this.messenger = new Telegram();
    }
}


public class Smartphone {

    private Application application;

    public Smartphone() {
        this.application = new Telegram();
    }
}
Maintenant, nous utilisons la Telegramclasse comme nous le voulons. À certains endroits, il agit comme un App. Dans d'autres endroits, il agit comme un Messenger. Vous avez sûrement déjà remarqué que les méthodes d'interface sont toujours "vides", c'est-à-dire qu'elles n'ont pas d'implémentation. La raison en est simple : l'interface décrit le comportement, mais elle ne l'implémente pas. 'Tous les objets qui implémentent l' CanSwiminterface doivent pouvoir nager' : c'est tout ce que nous dit l'interface. La manière spécifique dont les poissons, les canards et les chevaux nagent est une question pour les Fish, DucketHorseclasses, pas l'interface. Tout comme le changement de chaîne est une tâche pour le téléviseur. La télécommande vous donne simplement un bouton pour cela. Cependant, un ajout intéressant est apparu dans Java 8 — les méthodes par défaut. Par exemple, votre interface a 10 méthodes. 9 d'entre eux ont des implémentations différentes dans différentes classes, mais l'une est implémentée de la même manière pour tous. Auparavant, avant Java 8, les méthodes d'interface n'avaient aucune implémentation : le compilateur renvoyait immédiatement une erreur. Maintenant, vous pouvez faire quelque chose comme ceci :

public interface CanSwim {

   public default void swim() {
       System.out.println("Swim!");
   }

   public void eat();

   public void run();
}
En utilisant le defaultmot-clé, nous avons créé une méthode d'interface avec une implémentation par défaut. Nous devons fournir notre propre implémentation pour deux autres méthodes — eat()et run()— dans toutes les classes qui implémentent CanSwim. Nous n'avons pas besoin de faire cela avec la swim()méthode : l'implémentation sera la même dans chaque classe. Au fait, vous avez déjà rencontré des interfaces dans des tâches précédentes, même si vous ne l'aviez pas remarqué :) Voici un exemple frappant : Pourquoi les interfaces sont nécessaires en Java - 2vous avez travaillé avec les interfaces Listet ! SetPlus précisément, vous avez travaillé avec leurs implémentations — ArrayList, LinkedList, HashSet, etc. Le même diagramme donne clairement un exemple où une classe implémente plusieurs interfaces en même temps. Par exemple, LinkedListimplémente le ListetDeque(file d'attente double). Vous êtes familier avec l' Mapinterface, ou plutôt, avec sa HashMapmise en œuvre. Soit dit en passant, ce schéma illustre une fonctionnalité : les interfaces peuvent hériter d'autres interfaces. L' SortedMapinterface hérite de Map, tandis que Dequehérite de Queue. Cela est nécessaire si vous souhaitez afficher la relation entre les interfaces, où une interface est une version étendue d'une autre. Prenons un exemple avec l' Queueinterface. Nous n'avons pas encore examinéQueues, mais c'est assez simple et fonctionne comme une file d'attente ordinaire dans un magasin. Vous ne pouvez ajouter des éléments qu'à la fin de la file d'attente et ne pouvez les prendre qu'à partir du début. À un moment donné, les développeurs ont eu besoin d'une version améliorée de la file d'attente afin d'ajouter et de prendre des éléments aux deux extrémités. Ils ont donc créé une Dequeinterface, qui est une file d'attente à double extrémité. Il a toutes les méthodes d'une file d'attente ordinaire. Après tout, c'est le parent de la file d'attente double, mais il ajoute également de nouvelles méthodes.
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION