Programovanie

Príliš veľa parametrov v metódach Java, časť 6: Metóda sa vracia

V aktuálnej sérii príspevkov, ktoré píšem o znížení počtu parametrov potrebných na volanie metód a konštruktorov Java, som sa zatiaľ sústredil na prístupy, ktoré priamo ovplyvňujú samotné parametre (vlastné typy, objekty parametrov, vzor staviteľa, preťaženie metód a pomenovanie metódy). Vzhľadom na to by sa mi mohlo zdať prekvapivé venovať sa príspevku v tejto sérii tomu, ako metódy Java poskytujú návratové hodnoty. Návratové hodnoty metód však môžu mať vplyv na parametre, ktoré metódy akceptujú, keď sa vývojári rozhodnú poskytnúť „návratové“ hodnoty nastavením alebo zmenou poskytnutých parametrov namiesto tradičných mechanizmov návratu metód alebo popri nich.

„Tradičné spôsoby“, ako metóda, ktorá nie je konštruktorom, vracia hodnotu, je možné určiť v podpise metódy. Najčastejšie uznávaným prístupom k vráteniu hodnoty z metódy Java je deklarovaný návratový typ. To často funguje dobre, ale jednou z frustrácií, ktorá sa najčastejšie vyskytuje, je povolenie vrátiť iba jednu hodnotu z metódy Java.

Mechanizmus spracovania výnimiek Java je tiež ďalším prístupom k udržaniu „výsledku“ metódy pre volajúcich. Volajúcemu sa inzerujú najmä kontrolované výnimky prostredníctvom klauzuly „throws“. V skutočnosti Jim Waldo vo svojej knihe Java: The Good Parts uvádza, že je jednoduchšie pochopiť výnimky Java, keď si niekto myslí o výnimkách Java ako o inom type návratu metódy, ktorý je obmedzený na typ Throwable.

Aj keď návratový typ metódy a vyvolané výnimky sú zamýšľané ako primárne prístupy k metódam vracania informácií volajúcim, niekedy je lákavé vrátiť dáta alebo stav prostredníctvom parametrov odovzdaných do metódy. Ak metóda potrebuje vrátiť viac ako jednu informáciu, zdajú sa byť návraty jednej hodnoty metódami Java limitujúce. Aj keď výnimky poskytujú iný spôsob spätnej komunikácie s volajúcim, zdá sa, že je všeobecne všeobecne známe, že výnimky by sa mali používať iba na hlásenie výnimočných situácií, a nie na hlásenie „bežných“ údajov, alebo sa používajú v rámci kontroly. Vzhľadom na to, že z metódy je možné vrátiť iba jeden objekt alebo primitív a že výnimky umožňujú iba návrat a Hoditeľné a mal by sa používať iba na hlásenie výnimočných situácií, pre vývojára Java je čoraz atraktívnejšie uniesť parametre ako alternatívnu cestu na vrátenie údajov volajúcemu.

Technika, ktorú môže vývojár použiť na použitie parametrov metódy ako nosičov pre návratové dáta, je prijať parametre, ktoré sú premenlivé, a mutovať stav odovzdaných objektov. Tieto premenlivé objekty môžu mať obsah zmenený metódou a potom môže volajúci získať prístup k objektu, ktorý poskytol, aby určil jeho nové nastavenia stavu, ktoré boli aplikované volanou metódou. Aj keď je to možné urobiť s ľubovoľným premenlivým objektom, kolekcie sa zdajú byť obzvlášť atraktívne pre vývojárov, ktorí sa snažia odovzdať hodnoty volajúcemu prostredníctvom parametrov.

Existujú určité nevýhody prechodu stavu späť do volaného prostredníctvom poskytnutých parametrov. Tento prístup často porušuje zásadu najmenšieho údivu, pretože väčšina vývojárov Java pravdepodobne očakáva, že parametre budú skôr prichádzajúce ako odchádzajúce (a jazyk Java neposkytuje žiadnu podporu kódu na určenie rozdielu). Bob Martin to vyjadril vo svojej knihe Čistý kód: „Všeobecne by sa malo zabrániť výstupným argumentom.“ Ďalšou nevýhodou použitia argumentov ako prostriedku na poskytnutie stavu alebo výstupu volajúcemu je to, že sa tým zvyšuje neporiadok argumentov odovzdávaných metóde. S ohľadom na to sa zvyšok tohto príspevku zameriava na alternatívy vrátenia viacerých hodnôt prostredníctvom odovzdaných parametrov.

Aj keď metódy Java môžu vrátiť iba jeden objekt alebo primitív, nie je to naozaj nijaké obmedzenie, keď sa domnievame, že objekt môže byť takmer všetkým, čo chceme. Existuje niekoľko prístupov, ktoré som videl, ale neodporúčam. Jedným z nich je vrátenie poľa alebo kolekcie inštancií Object s každou z nich Objekt byť nesúrodou a zreteľnou a často nesúvisiacou „vecou“. Metóda môže napríklad vrátiť tri hodnoty ako tri prvky poľa alebo kolekcie. Variantom tohto prístupu je použitie dvojice alebo n-tice na vrátenie viacerých súvisiacich hodnôt. Jednou z ďalších variácií tohto prístupu je vrátenie mapy Java Map, ktorá mapuje ľubovoľné kľúče na ich priradenú hodnotu. Rovnako ako v prípade iných riešení, aj tento prístup predstavuje pre klienta neprimeranú záťaž, aby vedel, čo sú tieto kľúče, a aby prostredníctvom týchto kľúčov získal prístup k hodnotám mapy.

Nasledujúci zoznam kódov obsahuje niekoľko z týchto menej atraktívnych prístupov k vráteniu viacerých hodnôt bez únosu parametrov metódy na vrátenie viacerých hodnôt.

Vrátenie viacerých hodnôt prostredníctvom všeobecných dátových štruktúr

 // ================================================= ================ // POZNÁMKA: Tieto príklady slúžia iba na ilustráciu bodu // a NIE sa odporúčajú pre produkčný kód. // ================================================= ================ / ** * Poskytnite informácie o filme. * * @return Informácie o filme vo forme poľa, v ktorom sú podrobnosti mapované na * prvky s nasledujúcimi indexmi v poli: * 0: Názov filmu * 1: Rok vydania * 2: Režisér * 3: Hodnotenie * / verejný objekt [] getMovieInformation () {final Object [] movieDetails = {"World War Z", 2013, "Marc Forster", "PG-13"}; vrátiť filmDetails; } / ** * Poskytnite informácie o filme. * * @return Informácie o filme vo forme zoznamu, v ktorom sú uvedené podrobnosti * v tomto poradí: Názov filmu, Rok uvedenia, Režisér, Hodnotenie. * / public List getMovieDetails () {return Arrays.asList ("Ender's Game", 2013, "Gavin Hood", "PG-13"); } / ** * Poskytnite informácie o filme. * * @return Informácie o filme vo forme mapy. Charakteristiky filmu * je možné získať * vyhľadaním na mape na mape týchto kľúčových prvkov: „Názov“, „Rok“, * „Režisér“ a „Hodnotenie“ ./ * / public Map getMovieDetailsMap () {final HashMap map = new HashMap (); map.put ("Názov", "Despicable Me 2"); map.put („Rok“, 2013); map.put („riaditeľ“, „Pierre Coffin a Chris Renaud“); map.put ("Hodnotenie", "PG"); spiatočná mapa; } 

Prístupy uvedené vyššie spĺňajú zámer nepredávať dáta späť volajúcemu prostredníctvom parametrov vyvolaných metód, ale stále existuje zbytočné zaťaženie volajúceho, aby vedel dôverné podrobnosti vrátenej dátovej štruktúry. Je fajn znížiť počet parametrov na metódu a neporušiť zásadu najmenšieho prekvapenia, ale nie je také pekné vyžadovať od klienta, aby poznal zložitosť zložitej dátovej štruktúry.

Dávam prednosť písaniu vlastných objektov pre svoje vrátenia, keď potrebujem vrátiť viac ako jednu hodnotu. Je to o niečo viac práce ako s použitím štruktúry poľa, kolekcie alebo n-tice, ale veľmi malé množstvo práce navyše (zvyčajne niekoľko minút s modernými Java IDE) sa vyplatí s čitateľnosťou a plynulosťou, ktoré nie sú k dispozícii pri týchto všeobecnejších prístupoch. Namiesto toho, aby som musel vysvetľovať s Javadocom alebo vyžadovať od používateľov môjho kódu, aby si pozorne prečítali môj kód, aby vedeli, ktoré parametre sú poskytované v akom poradí v poli alebo kolekcii alebo ktorá hodnota je v n-tici, môžu moje vlastné návratové objekty mať definované metódy tí, ktorí klientovi povedia presne to, čo poskytuje.

Nasledujúce úryvky kódu ilustrujú jednoduchosť Film trieda zväčša generovaná NetBeans, ktorú je možné použiť ako návratový typ spolu s kódom, ktorý by mohol vrátiť inštanciu danej triedy, a nie všeobecnejšiu a menej čitateľnú dátovú štruktúru.

Movie.java

balenie zásypu.príklady; import java.util.Objects; / ** * Trieda Simple Movie, ktorá demonštruje, aké ľahké je poskytnúť viac hodnôt * v rámci jednej metódy vrátenia Java a zabezpečiť čitateľnosť pre klienta. * * @author Dustin * / public class Movie {private final String movieTitle; súkromný posledný int rok Vydané; súkromný konečný reťazec movieDirectorName; súkromné ​​finále 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 () {návrat movieDirectorName; } public String getMovieRating () {návrat 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); návratový 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; } návrat pravdivý; } @Override public String toString () {return "Movie {" + "movieTitle =" + movieTitle + ", yearReleased =" + yearReleased + ", movieDirectorName =" + movieDirectorName + ", movieRating =" + movieRating + '}'; }} 

Vrátenie viacerých podrobností v jednom objekte

 / ** * Poskytnite informácie o filme. * * @return Informácie o filme. * / public Movie getMovieInfo () {návrat nového filmu ("Oblivion", 2013, "Joseph Kosinski", "PG-13"); } 

Jednoduché písanie Film trieda mi trvala asi 5 minút. Pomocou sprievodcu vytvorením triedy NetBeans som vybral názov triedy a balíček a potom som zadal štyri atribúty triedy. Odtiaľ som jednoducho použil mechanizmus „Vložte kód“ NetBeans na vloženie prístupových metód „get“ spolu s prepísanými metódami toString (), hashCode () a equals (Object). Ak by som si nemyslel, že niečo z toho potrebujem, mohol by som triedu zjednodušiť, ale skutočne sa dá ľahko vytvoriť tak, ako je. Teraz mám oveľa použiteľnejší návratový typ a odráža to kód, ktorý používa triedu. Na návratový typ nepotrebuje zďaleka toľko komentárov Javadocu, pretože tento typ hovorí sám za seba a inzeruje svoj obsah metódami „get“. Mám pocit, že malé množstvo dodatočného úsilia vytvoriť tieto jednoduché triedy na vrátenie viacerých hodnôt sa vyplatí s obrovskými dividendami v porovnaní s alternatívami, ako je návrat stavu pomocou parametrov metódy alebo použitie všeobecnejších a ťažšie použiteľných štruktúr návratových údajov.

Nie je príliš prekvapujúce, že vlastný typ, ktorý obsahuje viac hodnôt, ktoré sa majú vrátiť volajúcemu, je atraktívnym riešením. Koniec koncov, toto je koncepčne veľmi podobné konceptom, o ktorých som predtým písal v blogoch, týkajúcich sa používania vlastných typov a parametrov objektov na odovzdávanie viacerých súvisiacich parametrov namiesto ich individuálneho odovzdávania. Java je objektovo orientovaný jazyk, a preto ma prekvapuje, keď nevidím objekty, ktoré sa v kóde Java používajú častejšie na usporiadanie parametrov A návratových hodnôt v peknom balíku.

Výhody a výhody

Výhody použitia objektov vlastných parametrov na reprezentáciu a zapuzdrenie viacerých návratových hodnôt sú zrejmé. Parametre metódy môžu zostať „vstupnými“ parametrami, pretože všetky výstupné informácie (okrem informácií o chybách komunikovaných prostredníctvom mechanizmu výnimiek) je možné poskytnúť vo vlastnom objekte vrátenom metódou. Jedná sa o čistejší prístup ako použitie všeobecných polí, zbierok, máp, n-tic alebo iných všeobecných dátových štruktúr, pretože všetky tieto alternatívne prístupy presúvajú vývojové úsilie na všetkých potenciálnych klientov.

Náklady a nevýhody

Vidím veľmi malú nevýhodu pri písaní vlastných typov s viacerými hodnotami, ktoré sa majú použiť ako návratové typy z metód Java. Snáď najčastejšie uvádzanou cenou je cena za napísanie a testovanie týchto tried, ale táto cena je dosť malá, pretože tieto triedy bývajú jednoduché a pretože moderné IDE za nás robia väčšinu práce. Pretože IDE to robia automaticky, kód je zvyčajne správny. Triedy sú také jednoduché, že ich čitatelia kódu ľahko prečítajú a dajú sa ľahko testovať.

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