Previše parametara u Java metodama, 6. dio: Povratak metode

U trenutnoj seriji postova o kojima pišem o smanjenju broja parametara potrebnih za pozivanje Java metoda i konstruktora, do sada sam se usredotočio na pristupe koji izravno utječu na same parametre (prilagođeni tipovi, objekti parametara, obrazac graditelja, preopterećenje metode i imenovanje metoda). S obzirom na ovo, moglo bi mi izgledati iznenađujuće posvetiti post u ovoj seriji tome kako Java metode pružaju povratne vrijednosti. Međutim, povratne vrijednosti metoda mogu utjecati na parametre koje metode prihvaćaju kada programeri odluče pružiti "povratne" vrijednosti postavljanjem ili promjenom ponuđenih parametara, umjesto ili kao dodatak tradicionalnijim mehanizmima povratka metode.

U potpisu metode mogu se navesti "tradicionalni načini" da metoda koja nije konstruktor vraća vrijednost. Najčešći pristup vraćanju vrijednosti iz Java metode je putem deklariranog tipa return. To često djeluje dobro, ali jedna od frustracija koja se najčešće događa jest dopuštanje vraćanja samo jedne vrijednosti iz Java metode.

Javin mehanizam za rukovanje iznimkama također je još jedan pristup za zadržavanje "rezultata" metode kod pozivatelja. Provjerene iznimke posebno se oglašavaju pozivatelju putem klauzule o bacanju. Zapravo, Jim Waldo u svojoj knjizi Java: Dobri dijelovi navodi da je lakše razumjeti Java iznimke kad se Java iznimke smatraju drugom vrstom metode koja se ograničava na vrstu Throwable.

Iako su vrsta povratka metode i izbačene iznimke zamišljeni kao primarni pristupi metodama za vraćanje informacija pozivaocima, ponekad je primamljivo vratiti podatke ili stanje putem parametara proslijeđenih u metodu. Kada metoda treba vratiti više podataka, povrat Java vrijednosti u jednu vrijednost može se činiti ograničavajućim. Iako iznimke pružaju drugi način za povratnu komunikaciju s pozivateljem, čini se gotovo općeprihvaćenim da se iznimke trebaju koristiti samo za prijavljivanje izvanrednih situacija, a ne za izvještavanje o "normalnim" podacima ili koristiti u protoku kontrole. S obzirom na to da se iz metode može vratiti samo jedan objekt ili primitiv i da iznimke dopuštaju samo vraćanje aThrowable i treba se koristiti samo za izvještavanje o iznimnim situacijama, programeru Java postaje sve atraktivnije otimati parametre kao zamjensku rutu za vraćanje podataka pozivatelju.

Tehnika koju programer može koristiti za primjenu parametara metode kao nosača podataka za povrat je prihvaćanje parametara koji su promjenjivi i mutiranje stanja predanih objekata. Metoda može promijeniti sadržaj ovih promjenjivih objekata, a zatim pozivatelj može pristupiti objektu koji je pružio kako bi odredio nove postavke stanja koje su primijenjene pozvanom metodom. Iako se to može učiniti s bilo kojim promjenjivim objektom, zbirke se čine posebno privlačnima programeru koji pokušava vratiti vrijednosti pozivatelju putem parametara.

Postoje neki nedostaci vraćanja stanja u pozvano putem navedenih parametara. Ovaj pristup često krši načelo najmanjeg zaprepaštenja jer većina programera Jave vjerojatno očekuje da će parametri biti ulazni, a ne izlazni (a Java ne pruža nikakvu podršku koda za specificiranje razlike). Bob Martin to u svojoj knjizi Clean Code kaže: "Općenito, valja izbjegavati izlazne argumente." Sljedeći nedostatak korištenja argumenata kao sredstva za metodu koja pruža stanje ili izlaz pozivatelju je taj što dodaje nered argumenata proslijeđenih metodi. Imajući to na umu, ostatak ovog posta usredotočen je na alternative vraćanju više vrijednosti putem prenesenih parametara.

Iako Java metode mogu vratiti samo jedan objekt ili primitiv, to doista nije veliko ograničenje kad se uzme u obzir da objekt može biti gotovo sve što mi želimo. Postoji nekoliko pristupa koje sam vidio, ali ih ne preporučujem. Jedan od njih je vraćanje niza ili kolekcije primjeraka Objekta sa svakimObjectbiti različita i različita i često nepovezana "stvar". Na primjer, metoda može vratiti tri vrijednosti kao tri elementa niza ili zbirke. Varijacija ovog pristupa je upotreba parice korpice ili korpice veličine n za vraćanje više pridruženih vrijednosti. Još jedna varijacija ovog pristupa je vraćanje Java Mape koja preslikava proizvoljne ključeve na njihovu pridruženu vrijednost. Kao i kod ostalih rješenja, ovaj pristup klijentu pretjerano opterećuje da zna koji su to ključevi i da putem tih ključeva pristupi vrijednostima karte.

Sljedeći popis koda sadrži nekoliko ovih manje atraktivnih pristupa za vraćanje više vrijednosti bez otmice parametara metode za vraćanje više vrijednosti.

Vraćanje višestrukih vrijednosti putem generičkih struktura podataka

 // =============================================================== // NOTE: These examples are intended solely to illustrate a point // and are NOT recommended for production code. // =============================================================== /** * Provide movie information. * * @return Movie information in form of an array where details are mapped to * elements with the following indexes in the array: * 0 : Movie Title * 1 : Year Released * 2 : Director * 3 : Rating */ public Object[] getMovieInformation() { final Object[] movieDetails = {"World War Z", 2013, "Marc Forster", "PG-13"}; return movieDetails; } /** * Provide movie information. * * @return Movie information in form of a List where details are provided * in this order: Movie Title, Year Released, Director, Rating. */ public List getMovieDetails() { return Arrays.asList("Ender's Game", 2013, "Gavin Hood", "PG-13"); } /** * Provide movie information. * * @return Movie information in Map form. Characteristics of the movie can * be acquired by looking in the map for these key elements: "Title", "Year", * "Director", and "Rating"./ */ public Map getMovieDetailsMap() { final HashMap map = new HashMap(); map.put("Title", "Despicable Me 2"); map.put("Year", 2013); map.put("Director", "Pierre Coffin and Chris Renaud"); map.put("Rating", "PG"); return map; } 

Gore prikazani pristupi ispunjavaju namjeru da se podaci ne vraćaju pozivatelju putem parametara pozvanih metoda, ali pozivatelju još uvijek ostaje nepotrebno opterećenje da zna intimne detalje vraćene strukture podataka. Lijepo je smanjiti broj parametara na metodu i ne kršiti načelo najmanjeg iznenađenja, ali nije tako lijepo zahtijevati od klijenta da zna zamršenosti složene strukture podataka.

Radije pišem prilagođene objekte za svoje povratke kada moram vratiti više od jedne vrijednosti. To je malo više posla od korištenja niza, zbirke ili strukture korice, ali vrlo mala količina dodatnog posla (obično nekoliko minuta s modernim Java IDE-ima) isplati se čitljivošću i fluidnošću koja nije dostupna kod ovih generičnijih pristupa. Umjesto da moram objašnjavati s Javadocom ili zahtijevati od korisnika mog koda da pažljivo čitaju moj kôd kako bi znali koji se parametri u kojem redoslijedu navode u nizu ili zbirci ili koja je vrijednost u tupleu, moji prilagođeni povratni objekti mogu imati definirane metode na oni koji klijentu točno kažu što im pružaju.

Isječci koda koji slijede ilustriraju jednostavnu Movieklasu koju su u velikoj mjeri generirali NetBeans i koja se može koristiti kao povratni tip zajedno s kodom koji može vratiti instancu te klase, a ne generičku i manje čitljivu strukturu podataka.

Film.java

package dustin.examples; import java.util.Objects; /** * Simple Movie class to demonstrate how easy it is to provide multiple values * in a single Java method return and provide readability to the client. * * @author Dustin */ public class Movie { private final String movieTitle; private final int yearReleased; private final String movieDirectorName; private final String movieRating; public Movie(String movieTitle, int yearReleased, String movieDirectorName, String movieRating) { this.movieTitle = movieTitle; this.yearReleased = yearReleased; this.movieDirectorName = movieDirectorName; this.movieRating = movieRating; } public String getMovieTitle() { return movieTitle; } public int getYearReleased() { return yearReleased; } public String getMovieDirectorName() { return movieDirectorName; } public 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); return hash; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final Movie other = (Movie) obj; if (!Objects.equals(this.movieTitle, other.movieTitle)) { return false; } if (this.yearReleased != other.yearReleased) { return false; } if (!Objects.equals(this.movieDirectorName, other.movieDirectorName)) { return false; } if (!Objects.equals(this.movieRating, other.movieRating)) { return false; } return true; } @Override public String toString() { return "Movie{" + "movieTitle=" + movieTitle + ", yearReleased=" + yearReleased + ", movieDirectorName=" + movieDirectorName + ", movieRating=" + movieRating + '}'; } } 

Vraćanje više pojedinosti u jednom objektu

 /** * Provide movie information. * * @return Movie information. */ public Movie getMovieInfo() { return new Movie("Oblivion", 2013, "Joseph Kosinski", "PG-13"); } 

Jednostavno pisanje Moviepredavanja mi je trebalo oko 5 minuta. Pomoću čarobnjaka za stvaranje klase NetBeans odabrao sam naziv klase i paket, a zatim sam ukucao četiri atributa klase. Odatle sam jednostavno koristio NetBeansov mehanizam "Umetni kod" za umetanje pristupnih metoda "get" zajedno s nadjačanim toString (), hashCode () i jednakim (Object) metodama. Da ne mislim da mi treba nešto od toga, mogao bih razred učiniti jednostavnijim, ali stvarno je lako stvoriti takav kakav jest. Sada imam puno korisniji tip povratka i to se odražava u kodu koji koristi klasu. Ne treba ni približno toliko Javadoc komentara o vrsti povratka jer ta vrsta govori sama za sebe i oglašava svoj sadržaj svojim metodama "get".Osjećam da se mali dodatni napor za stvaranje ovih jednostavnih klasa za vraćanje višestrukih vrijednosti isplati ogromnim dividendama u usporedbi s alternativama poput vraćanja stanja putem parametara metode ili korištenjem generičkih i težih za uporabu struktura podataka o povratu.

Nije previše iznenađujuće da je prilagođeni tip za zadržavanje višestrukih vrijednosti koje treba vratiti pozivatelju privlačno rješenje. Napokon, ovo je konceptualno vrlo slično konceptima o kojima sam prethodno napisao blog, a koji se odnose na upotrebu prilagođenih tipova i parametarskih objekata za prosljeđivanje više povezanih parametara, umjesto da ih sve prosljeđujem pojedinačno. Java je objektno orijentirani jezik i zato me iznenađuje kad ne vidim objekte koji se češće koriste u Java kodu za organiziranje parametara I vraćanje vrijednosti u lijepom paketu.

Prednosti i prednosti

Očiti su prednosti korištenja objekata prilagođenih parametara za predstavljanje i inkapsuliranje višestrukih povratnih vrijednosti. Parametri metode mogu ostati "ulazni" parametri jer se sve izlazne informacije (osim informacija o pogreškama koje se prenose putem mehanizma iznimke) mogu pružiti u prilagođenom objektu koji metoda vraća. Ovo je čistiji pristup od korištenja generičkih nizova, zbirki, mapa, korica ili drugih generičkih struktura podataka, jer svi ti alternativni pristupi preusmjeravaju razvojne napore na sve potencijalne klijente.

Troškovi i nedostaci

Vidim vrlo malo nedostataka u pisanju prilagođenih tipova s ​​više vrijednosti koji će se koristiti kao povratni tipovi iz Java metoda. Možda je trošak koji se najčešće tvrdi cijena pisanja i testiranja ovih klasa, ali taj je trošak prilično malen jer su te klase obično jednostavne i jer moderni IDE-i rade većinu posla za nas. Budući da IDE to rade automatski, kôd je obično točan. Predavanja su toliko jednostavna da ih recenzenti lako čitaju i lako ih je testirati.