CodeGym /Java-blogg /Tilfeldig /Hvorfor trenger vi grensesnitt i Java
John Squirrels
Nivå
San Francisco

Hvorfor trenger vi grensesnitt i Java

Publisert i gruppen
Hei! I dag skal vi snakke om et viktig konsept i Java: grensesnitt. Ordet er sikkert kjent for deg. For eksempel har de fleste dataprogrammer og spill grensesnitt. I vid forstand er et grensesnitt en slags 'fjernkontroll' som forbinder to samhandlende parter. Et enkelt eksempel på et grensesnitt i hverdagen er en TV-fjernkontroll. Den kobler sammen to objekter – en person og en TV – og utfører forskjellige oppgaver: skru opp eller ned volumet, bytt kanal og slå på eller av TV-en. Én part (personen) må få tilgang til grensesnittet (trykk på en knapp på fjernkontrollen) for å få den andre parten til å utføre handlingen. For eksempel for å få TV-en til å bytte til neste kanal. Dessuten gjør brukeren ikke t trenger å vite hvordan TV-en er organisert eller hvordan kanalbytteprosessen implementeres internt. Det eneste brukeren har tilgang til er grensesnittet. Hovedmålet er å oppnå ønsket resultat. Hva har dette med programmering og Java å gjøre? Alt :) Å lage et grensesnitt er veldig likt å lage en vanlig klasse, men i stedet bruke ordetklasse , angir vi ordet grensesnitt . La oss se på det enkleste Java-grensesnittet, se hvordan det fungerer, og hvorfor vi trenger det:

public interface CanSwim {

     public void swim();
}
Vi har laget et CanSwim -grensesnitt. Det er litt som fjernkontrollen vår, men med én 'knapp': swim()- metoden. Men hvordan bruker vi denne fjernkontrollen? For å gjøre dette må vi implementere en metode, altså vår fjernkontrollknapp. For å bruke et grensesnitt, må noen klasser i programmet vårt implementere metodene. La oss finne opp en klasse hvis objekter "kan svømme". For eksempel passer en Duck- klasse:

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();
    }
}
"Hva ser vi her? Duck -klassen er 'assosiert' med CanSwim- grensesnittet ved hjelp av nøkkelordet implements . Du husker kanskje at vi brukte en lignende mekanisme for å assosiere to klasser gjennom arv, men i så fall brukte vi ordet utvider. For fullstendig klarhet kan vi oversette ' public class Duck implements CanSwim ' bokstavelig talt som: 'The public Duck -klassen implementerer CanSwim- grensesnittet'. Dette betyr at en klasse tilknyttet et grensesnitt må implementere alle metodene. Merk: vår Duckklasse, akkurat som grensesnittet CanSwim, har en swim()metode, og det inneholder noe logikk. Dette er et obligatorisk krav. Hvis vi bare skriverpublic class Duck implements CanSwimuten å lage en swim()metode i Duckklassen vil kompilatoren gi oss en feil: Duck er ikke abstrakt og overstyrer ikke abstrakt metode swim() i CanSwim Hvorfor? Hvorfor skjer dette? Hvis vi forklarer feilen ved å bruke TV-eksemplet, vil det være som å gi noen en TV-fjernkontroll med en "endre kanal"-knapp som ikke kan bytte kanal. Du kan trykke på knappen så mye du vil, men det vil ikke fungere. Fjernkontrollen bytter ikke kanal av seg selv: den sender bare et signal til TV-en, som implementerer den komplekse prosessen med kanalbytte. Og slik er det med anda vår: den må kunne svømme slik at den kan kalles ved hjelp av CanSwimgrensesnittet. Hvis den ikke vet hvordan,CanSwimgrensesnittet forbinder ikke de to partene – personen og programmet. Personen vil ikke kunne bruke swim()metoden til å svømme Duckinne i programmet. Nå forstår du tydeligere hva grensesnitt er for. Et grensesnitt beskriver atferden som klasser som implementerer grensesnittet må ha. 'Behavior' er en samling metoder. Hvis vi ønsker å lage flere budbringere, er det enkleste å lage et Messengergrensesnitt. Hva trenger hver budbringer? På et grunnleggende nivå skal de kunne motta og sende meldinger.

public interface Messenger{

     public void sendMessage();

     public void getMessage();
}
Nå kan vi ganske enkelt lage våre messenger-klasser som implementerer det tilsvarende grensesnittet. Selve kompilatoren vil "tvinge" oss til å implementere dem i klassene våre. 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!");
     }
}
Hva skjer:

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 fordeler gir dette? Den viktigste av dem er løs kobling. Tenk deg at vi designer et program som samler inn klientdata. Klassen Clienttrenger definitivt et felt for å indikere hvilken spesifikk messenger klienten bruker. Uten grensesnitt ville dette sett rart ut:

public class Client {

    private WhatsApp whatsApp;
    private Telegram telegram;
    private Viber viber;
}
Vi opprettet tre felt, men en klient kan bare ha én messenger. Vi vet bare ikke hvilken. Så vi må legge til alle muligheter til klassen for å kunne kommunisere med klienten. Det viser seg at en eller to av dem alltid vil være null, helt unødvendig av programmet. Det er bedre å bruke grensesnittet vårt i stedet:

public class Client {

    private Messenger messenger;
}
Dette er et eksempel på løs kobling! I stedet for å spesifisere en spesifikk messenger-klasse i Clientklassen, indikerer vi bare at klienten har en messenger. Hvilken nøyaktig vil bli bestemt mens programmet kjører. Men hvorfor trenger vi grensesnitt for dette? Hvorfor ble de i det hele tatt lagt til språket? Det er et godt spørsmål - og det riktige spørsmålet! Kan vi ikke oppnå samme resultat ved bruk av vanlig arv? Klassen Messengersom forelder, og Viber, Telegram, og WhatsAppsom barn. Det er faktisk mulig. Men det er en hake. Som du allerede vet, har Java ingen multippel arv. Men det er støtte for flere grensesnitt. En klasse kan implementere så mange grensesnitt du vil. Tenk deg at vi har en Smartphoneklasse som har enAppfelt, som representerer en app installert på smarttelefonen.

public class Smartphone {

    private App app;
}
Selvfølgelig er en app og en messenger like, men de er fortsatt forskjellige ting. Det kan være mobil- og desktopversjoner av en messenger, men appen representerer spesifikt en mobilapp. Her er avtalen - hvis vi brukte arv, ville vi ikke kunne legge til et Telegramobjekt i Smartphoneklassen. Tross alt Telegramkan ikke klassen samtidig arve Appog Messenger! Og vi har allerede fått det til å arve Messengerog lagt det til i Clientklassen. Men Telegramklassen kan enkelt implementere begge grensesnittene! Følgelig kan vi gi Clientklassen et Telegramobjekt som en Messenger, og vi kan gi den til Smartphoneklassen som en App. Slik gjø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();
    }
}
Nå bruker vi Telegramklassen slik vi vil. Noen steder fungerer den som en App. Andre steder fungerer den som en Messenger. Du har sikkert allerede lagt merke til at grensesnittmetoder alltid er "tomme", dvs. de har ingen implementering. Grunnen til dette er enkel: grensesnittet beskriver atferd, men det implementerer det ikke. 'Alle objekter som implementerer grensesnittet CanSwimmå kunne svømme': det er alt grensesnittet forteller oss. Den spesifikke måten fisk, ender og hester svømmer på er et spørsmål for Fish, Duck, ogHorseklasser, ikke grensesnittet. Akkurat som å bytte kanal er en oppgave for TV-en. Fjernkontrollen gir deg ganske enkelt en knapp for dette. Imidlertid dukket et interessant tillegg opp i Java 8 - standardmetoder. For eksempel har grensesnittet ditt 10 metoder. 9 av dem har forskjellige implementeringer i forskjellige klasser, men en er implementert lik for alle. Tidligere, før Java 8, hadde grensesnittmetoder ingen implementering overhodet: kompilatoren ga umiddelbart en feil. Nå kan du gjøre noe slikt:

public interface CanSwim {

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

   public void eat();

   public void run();
}
Ved å bruke defaultnøkkelordet har vi laget en grensesnittmetode med en standardimplementering. Vi må sørge for vår egen implementering for to andre metoder – eat()og run()– i alle klasser som implementerer CanSwim. Vi trenger ikke å gjøre dette med swim()metoden: implementeringen vil være den samme i hver klasse. Forresten, du har allerede støtt på grensesnitt i tidligere oppgaver, selv om du ikke la merke til det :) Her er et levende eksempel: Hvorfor grensesnitt er nødvendig i Java - 2Du har jobbet med grensesnittene Listog Set! Mer presist, du har jobbet med deres implementeringer — ArrayList, LinkedList, HashSet, osv. Det samme diagrammet gir tydelig et eksempel der en klasse implementerer flere grensesnitt samtidig. For eksempel LinkedListimplementerer ListogDeque(dobbeltende kø) grensesnitt. Du er kjent med grensesnittet Map, eller rettere sagt, med implementeringen HashMap. Forresten, dette diagrammet illustrerer en funksjon: grensesnitt kan arve andre grensesnitt. Grensesnittet SortedMaparver Map, mens Dequedet arver Queue. Dette er nødvendig hvis du ønsker å vise forholdet mellom grensesnitt, der ett grensesnitt er en utvidet versjon av et annet. La oss vurdere et eksempel med Queuegrensesnittet. Vi har ikke anmeldt ennåQueues, men det er ganske enkelt og fungerer som en vanlig kø, eller linje, i en butikk. Du kan bare legge til elementer på slutten av køen, og kan bare ta dem fra begynnelsen. På et tidspunkt trengte utviklere en forbedret versjon av køen for å legge til og ta elementer i begge ender. Så de opprettet et Dequegrensesnitt, som er en dobbel-ended kø. Den har alle metodene til en vanlig kø. Tross alt er det overordnet til den doble køen, men den legger også til nye metoder.
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION