CodeGym /Java blog /Tilfældig /Hvorfor har vi brug for grænseflader i Java
John Squirrels
Niveau
San Francisco

Hvorfor har vi brug for grænseflader i Java

Udgivet i gruppen
Hej! I dag skal vi tale om et vigtigt koncept i Java: grænseflader. Ordet er sikkert bekendt for dig. For eksempel har de fleste computerprogrammer og spil grænseflader. I bred forstand er en grænseflade en slags 'fjernbetjening', der forbinder to interagerende parter. Et simpelt eksempel på en grænseflade i hverdagen er en tv-fjernbetjening. Den forbinder to objekter - en person og et tv - og udfører forskellige opgaver: skru op eller ned for lydstyrken, skift kanal og tænd eller sluk for tv'et. Den ene part (personen) skal have adgang til grænsefladen (tryk på en knap på fjernbetjeningen) for at få den anden part til at udføre handlingen. For eksempel for at få tv'et til at skifte til næste kanal. Hvad mere er, gør brugeren ikke t brug for at vide, hvordan tv'et er organiseret, eller hvordan kanalskifteprocessen implementeres internt. Det eneste, brugeren har adgang til, er grænsefladen. Hovedformålet er at opnå det ønskede resultat. Hvad har det med programmering og Java at gøre? Alt :) At lave en grænseflade minder meget om at lave en almindelig klasse, men i stedet bruge ordetklasse , angiver vi ordet interface . Lad os se på den enkleste Java-grænseflade, se, hvordan den fungerer, og hvorfor vi har brug for den:

public interface CanSwim {

     public void swim();
}
Vi har lavet en CanSwim- grænseflade. Det er lidt ligesom vores fjernbetjening, men med én 'knap': swim()- metoden. Men hvordan bruger vi denne fjernbetjening? For at gøre dette skal vi implementere en metode, altså vores fjernbetjeningsknap. For at bruge en grænseflade skal nogle klasser i vores program implementere dens metoder. Lad os opfinde en klasse, hvis objekter 'kan svømme'. For eksempel passer en Duck- klasse til:

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();
    }
}
"Hvad ser vi her? Duck -klassen er 'associeret' med CanSwim- grænsefladen ved hjælp af nøgleordet implements . Du husker måske, at vi brugte en lignende mekanisme til at forbinde to klasser gennem arv, men i så fald brugte vi ordet udvider. For fuldstændig klarhed, vi kan oversætte ' public class Duck implements CanSwim ' bogstaveligt som: 'Den offentlige Duck- klasse implementerer CanSwim- grænsefladen'. Det betyder, at en klasse, der er knyttet til en grænseflade, skal implementere alle dens metoder. Bemærk: vores Duckklasse, ligesom grænsefladen CanSwim, har en swim()metode, og den indeholder noget logik. Dette er et obligatorisk krav. Hvis vi bare skriverpublic class Duck implements CanSwimuden at oprette en swim()metode i Duckklassen, vil compileren give os en fejl: Duck er ikke abstrakt og tilsidesætter ikke abstrakt metode swim() i CanSwim Hvorfor? Hvorfor sker dette? Hvis vi forklarer fejlen ved hjælp af tv-eksemplet, ville det være som at give nogen en tv-fjernbetjening med en 'skift kanal'-knap, der ikke kan skifte kanal. Du kan trykke på knappen så meget du vil, men det virker ikke. Fjernbetjeningen skifter ikke kanal af sig selv: den sender kun et signal til tv'et, som implementerer den komplekse proces med kanalskifte. Og sådan er det med vores and: den skal kunne svømme, så den kan kaldes ved hjælp af grænsefladen CanSwim. Hvis den ikke ved hvordanCanSwimgrænsefladen forbinder ikke de to parter - personen og programmet. Personen vil ikke være i stand til at bruge swim()metoden til at lave en Ducksvømmetur inde i programmet. Nu forstår du mere klart, hvad grænseflader er til. En grænseflade beskriver den adfærd, som klasser, der implementerer grænsefladen, skal have. 'Behavior' er en samling af metoder. Hvis vi vil oprette flere budbringere, er den nemmeste ting at gøre at oprette en Messengergrænseflade. Hvad har enhver budbringer brug for? På et grundlæggende niveau skal de kunne modtage og sende beskeder.

public interface Messenger{

     public void sendMessage();

     public void getMessage();
}
Nu kan vi simpelthen oprette vores messenger-klasser, der implementerer den tilsvarende grænseflade. Compileren i sig selv vil 'tvinge' os til at implementere dem i vores klasser. Telegram:

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!");
     }
}
Hvilke fordele giver dette? Den vigtigste af dem er løs kobling. Forestil dig, at vi designer et program, der indsamler klientdata. Klassen Clienthar bestemt brug for et felt for at angive, hvilken specifik messenger klienten bruger. Uden grænseflader ville dette se mærkeligt ud:

public class Client {

    private WhatsApp whatsApp;
    private Telegram telegram;
    private Viber viber;
}
Vi oprettede tre felter, men en klient kan kun have én messenger. Vi ved bare ikke hvilken. Så vi er nødt til at tilføje alle muligheder til klassen for at kunne kommunikere med klienten. Det viser sig, at en eller to af dem altid vil være null, helt unødvendige af programmet. Det er bedre at bruge vores grænseflade i stedet:

public class Client {

    private Messenger messenger;
}
Dette er et eksempel på løs kobling! I stedet for at angive en specifik messenger-klasse i Clientklassen, angiver vi blot, at klienten har en messenger. Hvilken præcis vil blive bestemt, mens programmet kører. Men hvorfor har vi brug for grænseflader til dette? Hvorfor blev de overhovedet føjet til sproget? Det er et godt spørgsmål - og det rigtige spørgsmål! Kan vi ikke opnå samme resultat ved at bruge almindelig arv? Klassen Messengersom forælder, og Viber, Telegram, og WhatsAppsom børn. Det er faktisk muligt. Men der er en hage. Som du allerede ved, har Java ingen multipel arv. Men der er understøttelse af flere grænseflader. En klasse kan implementere så mange grænseflader, som du vil. Forestil dig, at vi har en Smartphoneklasse, der har enAppfelt, som repræsenterer en app installeret på smartphonen.

public class Smartphone {

    private App app;
}
Selvfølgelig er en app og en messenger ens, men de er stadig forskellige ting. Der kan være mobil- og desktopversioner af en messenger, men App repræsenterer specifikt en mobilapp. Her er aftalen - hvis vi brugte arv, ville vi ikke være i stand til at tilføje et Telegramobjekt til Smartphoneklassen. Klassen kan jo Telegramikke samtidigt arve Appog Messenger! Og vi har allerede fået det til at arve Messengerog tilføjet det til Clientklassen. Men Telegramklassen kan nemt implementere begge grænseflader! Derfor kan vi give Clientklassen et Telegramobjekt som et Messenger, og vi kan give det til Smartphoneklassen som et App. Sådan gør du det:

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();
    }
}
Nu bruger vi Telegramklassen, som vi vil. Nogle steder fungerer den som en App. Andre steder fungerer den som en Messenger. Du har helt sikkert allerede bemærket, at grænseflademetoder altid er 'tomme', dvs. de har ingen implementering. Årsagen til dette er enkel: grænsefladen beskriver adfærd, men den implementerer den ikke. 'Alle objekter, der implementerer grænsefladen, CanSwimskal kunne svømme': det er alt, hvad grænsefladen fortæller os. Den specifikke måde, fisk, ænder og heste svømmer på, er et spørgsmål for Fish, Duck, ogHorseklasser, ikke grænsefladen. Ligesom at skifte kanal er en opgave for tv'et. Fjernbetjeningen giver dig blot en knap til dette. Imidlertid dukkede en interessant tilføjelse op i Java 8 - standardmetoder. For eksempel har din grænseflade 10 metoder. 9 af dem har forskellige implementeringer i forskellige klasser, men en er implementeret ens for alle. Tidligere, før Java 8, havde grænseflademetoder ingen implementering overhovedet: compileren gav straks en fejl. Nu kan du gøre sådan noget:

public interface CanSwim {

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

   public void eat();

   public void run();
}
Ved at bruge defaultnøgleordet har vi oprettet en grænseflademetode med en standardimplementering. Vi skal levere vores egen implementering til to andre metoder - eat()og run()- i alle klasser, der implementerer CanSwim. Vi behøver ikke at gøre dette med metoden swim(): implementeringen vil være den samme i hver klasse. I øvrigt er du allerede stødt på grænseflader i tidligere opgaver, selvom du ikke lagde mærke til det :) Her er et levende eksempel: Hvorfor grænseflader er nødvendige i Java - 2Du har arbejdet med Listog Setgrænseflader! Mere præcist har du arbejdet med deres implementeringer — ArrayList, LinkedList, HashSet, osv. Det samme diagram giver tydeligt et eksempel, hvor en klasse implementerer flere grænseflader på samme tid. For eksempel LinkedListimplementerer ListogDeque(dobbelt-endede kø) grænseflader. Du er bekendt med grænsefladen Map, eller rettere, med dens HashMapimplementering. Forresten illustrerer dette diagram en funktion: grænseflader kan arve andre grænseflader. Grænsefladen SortedMaparver Map, mens Dequeden arver Queue. Dette er nødvendigt, hvis du vil vise sammenhængen mellem grænseflader, hvor en grænseflade er en udvidet version af en anden. Lad os overveje et eksempel med grænsefladen Queue. Vi har endnu ikke anmeldtQueues, men det er ret simpelt og fungerer som en almindelig kø, eller linje, i en butik. Du kan kun tilføje elementer til slutningen af ​​køen og kan kun tage dem fra begyndelsen. På et tidspunkt havde udviklere brug for en forbedret version af køen for at tilføje og tage varer i begge ender. Så de skabte en Dequegrænseflade, som er en dobbeltkø. Den har alle metoderne til en almindelig kø. Det er trods alt forælderen til den dobbelte kø, men den tilføjer også nye metoder.
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION