Programmering

For mange parametere i Java-metoder, del 6: Metoden returnerer

I den nåværende innleggsserien jeg skriver om å redusere antall parametere som kreves for å kalle Java-metoder og konstruktører, har jeg hittil fokusert på tilnærminger som direkte påvirker parametrene selv (egendefinerte typer, parametere objekter, byggmønster, metodeoverbelastning og metodenavn). Gitt dette kan det virke overraskende for meg å vie et innlegg i denne serien til hvordan Java-metoder gir returverdier. Metodenes returverdier kan imidlertid påvirke parametrene metodene godtar når utviklere velger å gi "retur" -verdier ved å angi eller endre angitte parametre i stedet for eller i tillegg til mer tradisjonelle metode-returmekanismer.

De "tradisjonelle måtene" som en ikke-konstruktormetode returnerer en verdi kan begge spesifiseres i metodesignaturen. Den mest anerkjente tilnærmingen for å returnere en verdi fra en Java-metode er via den deklarerte returtypen. Dette fungerer ofte bra, men en av frustrasjonene som ofte oppstår, er å få lov til å returnere bare en verdi fra en Java-metode.

Java sin unntakshåndteringsmekanisme er også en annen tilnærming for å beholde et "resultat" av en metode for innringere. Spesielt sjekkede unntak blir annonsert for den som ringer via kast-klausulen. Faktisk sier Jim Waldo i sin bok Java: The Good Parts at det er lettere å forstå Java-unntak når man tenker på Java-unntak som en annen type metode som er begrenset til å være en Throwable-type.

Selv om metodens returtype og kastede unntak er ment som de viktigste tilnærmingene for metoder for å returnere informasjon til innringere, er det noen ganger fristende å returnere data eller tilstand via parametrene som sendes til metoden. Når en metode trenger å returnere mer enn ett stykke informasjon, kan enkeltverdier av Java-metoder virke begrensende. Selv om unntak gir en annen måte å kommunisere tilbake til den som ringer inn, virker det nesten allment enige om at unntak bare skal brukes til rapportering av eksepsjonelle situasjoner og ikke for rapportering av "normale" data eller brukes i kontrollflyt. Gitt at bare ett objekt eller primitive kan returneres fra en metode, og at unntak bare tillater retur av en Kastbar og skal bare brukes til å rapportere eksepsjonelle situasjoner, blir det stadig mer attraktivt for Java-utvikleren å kapre parametere som en alternativ rute for å returnere data til den som ringer.

Teknikken som en utvikler kan bruke for å anvende metodeparametere som bærere for returdata, er å akseptere parametere som er mutable og å mutere de innleverte objektenes tilstand. Disse foranderlige objektene kan ha endret innholdet etter metoden, og deretter kan den som ringer få tilgang til objektet den ga for å bestemme dens nye tilstandsinnstillinger som er blitt brukt etter den kallte metoden. Selv om dette kan gjøres med et hvilket som helst foranderlig objekt, virker samlingene spesielt attraktive for utvikleren som prøver å sende verdier tilbake til den som ringer via parametere.

Det er noen ulemper ved å sende tilstanden tilbake til den ringte via de angitte parametrene. Denne tilnærmingen bryter ofte med prinsippet om minst forbauselse, ettersom de fleste Java-utviklere sannsynligvis forventer at parametere skal være INNGående i stedet for UTgående (og Java gir ingen kodestøtte for å spesifisere forskjellen). Bob Martin uttrykker det slik i sin bok Clean Code, "Generelt sett bør outputargumenter unngås." En annen ulempe med å bruke argumenter som et middel for en metode for å gi tilstand eller utgang til den som ringer, er at dette legger til rotet av argumenter som sendes til en metode. Med dette i tankene fokuserer resten av dette innlegget på alternativer for å returnere flere verdier via innleverte parametere.

Selv om Java-metoder bare kan returnere et enkelt eller primitivt objekt, er dette egentlig ikke mye av en begrensning når man vurderer at et objekt kan være omtrent alt vi vil at det skal være. Det er flere tilnærminger jeg har sett, men ikke anbefaler. En av disse returnerer en matrise eller samling av objektforekomster med hver Gjenstand å være en forskjellig og tydelig og ofte ikke-relatert "ting". For eksempel kan metoden returnere tre verdier som tre elementer i en matrise eller samling. En variant av denne tilnærmingen er å bruke en par tuple eller n-størrelse tuple for å returnere flere tilknyttede verdier. En annen variant av denne tilnærmingen er å returnere et Java-kart som kartlegger vilkårlige nøkler til deres tilknyttede verdi. Som med de andre løsningene, legger denne tilnærmingen unødig byrde på klienten å vite hva disse nøklene er, og få tilgang til kartverdiene gjennom disse nøklene.

Den neste kodelisten inneholder flere av disse mindre attraktive tilnærmingene for å returnere flere verdier uten å kapre metodeparametrene for å returnere flere verdier.

Returnering av flere verdier via generiske datastrukturer

 // ==================================================== =============== // MERK: Disse eksemplene er kun ment for å illustrere et punkt // og anbefales IKKE for produksjonskode. // ==================================================== ================ / ** * Gi filminformasjon. * * @return Filminformasjon i form av en matrise der detaljer blir kartlagt til * elementer med følgende indekser i matrisen: * 0: Filmtittel * 1: År utgitt * 2: Regissør * 3: Vurdering * / offentlig objekt [] getMovieInformation () {final Object [] movieDetails = {"World War Z", 2013, "Marc Forster", "PG-13"}; returnere filmDetaljer; } / ** * Gi filminformasjon. * * @return Filminformasjon i form av en liste der detaljer gis * i denne rekkefølgen: Filmtittel, utgitt år, regissør, rangering. * / public List getMovieDetails () {return Arrays.asList ("Ender's Game", 2013, "Gavin Hood", "PG-13"); } / ** * Gi filminformasjon. * * @return Filminformasjon i kartform. Filmens egenskaper kan * tilegnes ved å se i kartet etter disse nøkkelelementene: "Tittel", "År", * "Regissør" og "Vurdering" ./ * / public Map getMovieDetailsMap () {final HashMap map = new HashMap (); map.put ("Tittel", "Foraktelig meg 2"); map.put ("Year", 2013); map.put ("Director", "Pierre Coffin and Chris Renaud"); map.put ("Rating", "PG"); retur kart; } 

Tilnærmingene vist ovenfor oppfyller intensjonen om ikke å sende data tilbake til den som ringer via parametrene for de påkalte metodene, men det er fortsatt unødvendig belastning for den som ringer for å kjenne intime detaljer om den returnerte datastrukturen. Det er hyggelig å redusere antall parametere til metoden og ikke bryte med prinsippet om minst overraskelse, men det er ikke så hyggelig å kreve at klienten kjenner til komplikasjonene i en kompleks datastruktur.

Jeg foretrekker å skrive egendefinerte objekter for returene mine når jeg trenger å returnere mer enn én verdi. Det er litt mer arbeid enn å bruke en matrise, samling eller tuppelstruktur, men den veldig lille mengden ekstra arbeid (vanligvis noen få minutter med moderne Java IDEer) lønner seg med lesbarhet og flyt som ikke er tilgjengelig med disse mer generiske tilnærmingene. I stedet for å måtte forklare med Javadoc eller kreve brukere av koden min å lese koden min nøye for å vite hvilke parametere som er gitt i hvilken rekkefølge i matrisen eller samlingen eller hvilken verdi som er i tupelen, kan mine egendefinerte returobjekter ha metoder definert på dem som forteller kunden nøyaktig hva de gir.

Kodebitene som følger illustrerer en enkel Film klasse generert i stor grad av NetBeans som kan brukes som returtype sammen med koden som kan returnere en forekomst av den klassen i stedet for en mer generisk og mindre lesbar datastruktur.

Movie.java

pakke dustin. eksempler; importere java.util.Objects; / ** * Enkel filmklasse for å demonstrere hvor enkelt det er å gi flere verdier * i en enkelt Java-metode returnerer og gir lesbarhet for klienten. * * @forfatter Dustin * / public class Movie {privat finale String movieTitle; privat avsluttende int år Utgitt; privat finale String movieDirectorName; privat finale String movieRating; public Movie (String movieTitle, int yearReleased, String movieDirectorName, String movieRating) {this.movieTitle = movieTitle; this.yearReleased = yearReleased; this.movieDirectorName = movieDirectorName; this.movieRating = movieRating; } offentlig String getMovieTitle () {return movieTitle; } public int getYearReleased () {return yearReleased; } offentlig streng getMovieDirectorName () {returner movieDirectorName; } offentlig String getMovieRating () {return movieRating; } @ Override public int hashCode () {int hash = 3; hash = 89 * hash + Objects.hashCode (this.movieTitle); hash = 89 * hash + this.yearReleased; hash = 89 * hash + Objects.hashCode (this.movieDirectorName); hash = 89 * hash + Objects.hashCode (this.movieRating); retur hash; } @Override offentlig boolsk er lik (Objekt obj) {if (obj == null) {return false; } hvis (getClass ()! = obj.getClass ()) {return false; } final Movie other = (Film) obj; hvis (! Objects.equals (this.movieTitle, other.movieTitle)) {return false; } hvis (this.yearReleased! = other.yearReleased) {return false; } hvis (! Objects.equals (this.movieDirectorName, other.movieDirectorName)) {return false; } hvis (! Objects.equals (this.movieRating, other.movieRating)) {return false; } returner sant; } @ Override public String toString () {return "Movie {" + "movieTitle =" + movieTitle + ", yearReleased =" + yearReleased + ", movieDirectorName =" + movieDirectorName + ", movieRating =" + movieRating + '}'; }} 

Returnerer flere detaljer i ett objekt

 / ** * Gi filminformasjon. * * @return Filminformasjon. * / public Movie getMovieInfo () {returner ny film ("Oblivion", 2013, "Joseph Kosinski", "PG-13"); } 

Den enkle skrivingen av Film klassen tok meg omtrent 5 minutter. Jeg brukte veiviseren for opprettelse av NetBeans-klasser for å velge kursnavn og pakke, og deretter skrev jeg inn de fire attributtene til klassen. Derfra brukte jeg ganske enkelt NetBeans "Insert Code" -mekanisme for å sette inn "get" tilgangsmetoder sammen med overstyrte toString (), hashCode () og lik (Object) metoder. Hvis jeg ikke trodde jeg trengte noe av det, kunne jeg holde timen enklere, men det er veldig enkelt å lage som det er. Nå har jeg en mye mer brukbar returtype, og dette reflekteres av koden som bruker klassen. Den trenger ikke nesten like mye Javadoc-kommentarer på returtypen fordi den typen snakker for seg selv og annonserer innholdet med "get" -metodene. Jeg føler at den lille mengden ekstra innsats for å lage disse enkle klassene for å returnere flere verdier lønner seg med enorme utbytter sammenlignet med alternativer som returstatus via metodeparametere eller bruk av mer generiske og vanskeligere å bruke returdatastrukturer.

Det er ikke så overraskende at en tilpasset type for å inneholde flere verdier som skal returneres til en innringer, er en attraktiv løsning. Tross alt er dette konseptuelt veldig likt konseptene jeg blogget om tidligere relatert til bruk av tilpassede typer og parameterobjekter for å sende inn flere relaterte parametere i stedet for å sende dem alle individuelt. Java er et objektorientert språk, så det overrasker meg når jeg ikke ser objekter som brukes oftere i Java-kode for å organisere parametere OG returnere verdier i en fin pakke.

Fordeler og fordeler

Fordelene ved å bruke egendefinerte parameterobjekter til å representere og kapsle inn flere returverdier er åpenbare. Parametere til metoden kan forbli "input" -parametere fordi all utdatainformasjon (unntatt feilinformasjon kommunisert via unntaksmekanismen) kan gis i det egendefinerte objektet som returneres av metoden. Dette er en renere tilnærming enn å bruke generiske matriser, samlinger, kart, tupler eller andre generiske datastrukturer fordi alle disse alternative tilnærmingene flytter utviklingsarbeidet til alle potensielle kunder.

Kostnader og ulemper

Jeg ser veldig lite ulemper med å skrive egendefinerte typer med flere verdier som skal brukes som returtyper fra Java-metoder. Kanskje er den ofte påkrevde kostnaden prisen for å skrive og teste disse klassene, men kostnaden er ganske liten fordi disse klassene pleier å være enkle og fordi moderne IDE-er gjør det meste av jobben for oss. Fordi IDEene gjør det automatisk, er koden vanligvis riktig. Klassene er så enkle at de lett kan leses av kodevurderere, og de er enkle å teste.

$config[zx-auto] not found$config[zx-overlay] not found