Programování

Příliš mnoho parametrů v metodách Java, část 6: Metoda se vrací

V současné sérii příspěvků, které píšu o snížení počtu parametrů potřebných k volání metod a konstruktorů Java, jsem se zatím soustředil na přístupy, které přímo ovlivňují samotné parametry (vlastní typy, objekty parametrů, vzor stavitele, přetížení metody a pojmenování metody). Vzhledem k tomu by se mi mohlo zdát překvapivé věnovat příspěvek v této sérii tomu, jak metody Java poskytují návratové hodnoty. Návratové hodnoty metod však mohou ovlivnit parametry, které metody přijímají, když se vývojáři rozhodnou poskytnout „návratové“ hodnoty nastavením nebo změnou poskytovaných parametrů, spíše než nebo navíc k tradičnějším mechanismům návratů metod.

„Tradiční způsoby“, že metoda bez konstruktoru vrací hodnotu, lze zadat v podpisu metody. Nejčastěji uznávaným přístupem pro vrácení hodnoty z metody Java je deklarovaný návratový typ. To často funguje dobře, ale jednou z frustrací, která se nejčastěji vyskytuje, je povolení vrátit pouze jednu hodnotu z metody Java.

Mechanismus zpracování výjimek Java je také dalším přístupem k uchování „výsledku“ metody volajícím. Zejména zaškrtnuté výjimky se volajícímu inzerují prostřednictvím klauzule throws. Ve skutečnosti Jim Waldo ve své knize Java: The Good Parts uvádí, že je snazší pochopit výjimky Java, když si někdo myslí o výjimkách Java jako o jiném typu návratu metody omezeném na typ Throwable.

Ačkoli návratový typ metody a vyvolané výjimky jsou určeny jako primární přístupy k metodám pro vracení informací volajícím, je někdy lákavé vrátit data nebo stav prostřednictvím parametrů předaných do metody. Když metoda potřebuje vrátit více než jednu informaci, může se zdát, že návraty jedné hodnoty metod Java jsou omezující. Ačkoli výjimky poskytují jiný způsob komunikace zpět volajícímu, zdá se téměř všeobecně dohodnuto, že výjimky by měly být použity pouze pro hlášení výjimečných situací a nikoli pro hlášení „běžných“ údajů nebo v řízení. Vzhledem k tomu, že z metody lze vrátit pouze jeden objekt nebo primitiv a že výjimky umožňují pouze vrácení a Vrhací a mělo by být použito pouze k hlášení výjimečných situací, stává se pro vývojáře prostředí Java stále atraktivnější unést parametry jako alternativní cestu pro vracení dat volajícímu.

Technika, kterou může vývojář použít k použití parametrů metody jako nosiče pro návratová data, je přijmout parametry, které jsou proměnlivé, a mutovat stav předaných objektů. U těchto proměnlivých objektů může být jejich obsah změněn metodou a volající pak může přistupovat k objektu, který poskytl, aby určil jeho nové nastavení stavu, které bylo aplikováno volanou metodou. I když to lze provést s libovolným proměnným objektem, kolekce se zdají zvláště atraktivní pro vývojáře, který se snaží předat hodnoty zpět volajícímu pomocí parametrů.

Existují určité nevýhody předávání stavu zpět volanému prostřednictvím poskytnutých parametrů. Tento přístup často porušuje zásadu nejmenšího úžasu, protože většina vývojářů Java pravděpodobně očekává, že parametry budou INcoming než OUTgoing (a Java neposkytuje žádnou podporu kódu pro určení rozdílu). Bob Martin to ve své knize Clean Code uvádí takto: „Obecně by se mělo výstupním argumentům vyhnout.“ Další nevýhodou použití argumentů jako prostředku pro metodu poskytování stavu nebo výstupu volajícímu je to, že se tím přidává k nepořádku argumentů předávaných metodě. S ohledem na to se zbytek tohoto příspěvku zaměřuje na alternativy k vrácení více hodnot prostřednictvím předaných parametrů.

Ačkoli metody Java mohou vracet pouze jeden objekt nebo primitiv, ve skutečnosti to není příliš omezení, když vezmeme v úvahu, že objekt může být téměř cokoli, co chceme. Existuje několik přístupů, které jsem viděl, ale nedoporučuji. Jedním z nich je vrácení pole nebo kolekce instancí Object s každým Objekt být nesourodou a odlišnou a často nesouvisející „věcí“. Například metoda může vrátit tři hodnoty jako tři prvky pole nebo kolekce. Variancí tohoto přístupu je použití dvojice n-tice nebo n-velikosti n-tice k vrácení více přidružených hodnot. Jednou z dalších variant tohoto přístupu je vrátit mapu Java Map, která mapuje libovolné klíče na jejich přidruženou hodnotu. Stejně jako u ostatních řešení tento přístup klade na klienta nepřiměřenou zátěž, aby věděl, co jsou tyto klíče, a aby prostřednictvím těchto klíčů získal přístup k hodnotám mapy.

Další výpis kódu obsahuje několik z těchto méně atraktivních přístupů pro vrácení více hodnot bez únosu parametrů metody pro vrácení více hodnot.

Vrácení více hodnot prostřednictvím obecných datových struktur

 // ================================================= ================ // POZNÁMKA: Tyto příklady jsou určeny pouze k ilustraci bodu // a NENÍ doporučeny pro produkční kód. // ================================================= ================ / ** * Uveďte informace o filmu. * * @return Informace o filmu ve formě pole, kde jsou podrobnosti mapovány na * prvky s následujícími indexy v poli: * 0: Název filmu * 1: Rok vydání * 2: Režisér * 3: Hodnocení * / veřejný objekt [] getMovieInformation () {final Object [] movieDetails = {"World War Z", 2013, "Marc Forster", "PG-13"}; vrátit filmDetails; } / ** * Poskytněte informace o filmu. * * @return Informace o filmu ve formě seznamu, kde jsou uvedeny podrobnosti * v tomto pořadí: Název filmu, Rok vydání, Režisér, Hodnocení. * / public List getMovieDetails () {return Arrays.asList ("Ender's Game", 2013, "Gavin Hood", "PG-13"); } / ** * Poskytněte informace o filmu. * * @return Informace o filmu ve formě mapy. Charakteristiky filmu * lze získat hledáním na mapě těchto klíčových prvků: „Název“, „Rok“, * „Režisér“ a „Hodnocení“ ./ * / public Map getMovieDetailsMap () {final HashMap map = new HashMap (); map.put ("Název", "Despicable Me 2"); map.put ("Rok", 2013); map.put ("ředitel", "Pierre Coffin a Chris Renaud"); map.put ("Hodnocení", "PG"); zpáteční mapa; } 

Přístupy uvedené výše splňují záměr nepředávat data zpět volajícímu prostřednictvím parametrů vyvolaných metod, ale na volajícího je stále zbytečné břemeno znát intimní podrobnosti vrácené datové struktury. Je hezké omezit počet parametrů na metodu a neporušovat zásadu nejmenšího překvapení, ale není tak hezké vyžadovat, aby klient znal složitost složité datové struktury.

Když potřebuji vrátit více než jednu hodnotu, dávám přednost psaní vlastních objektů pro mé vrácení. Je to trochu víc práce než použití matice, kolekce nebo struktury n-tice, ale velmi malé množství práce navíc (obvykle několik minut s moderními prostředími Java IDE) se vyplatí čitelností a plynulostí, která u těchto obecnějších přístupů není k dispozici. Spíše než muset vysvětlovat s Javadocem nebo vyžadovat, aby uživatelé mého kódu pečlivě přečetli můj kód, aby věděli, které parametry jsou poskytovány v jakém pořadí v poli nebo kolekci nebo která hodnota je v n-tice, mohou mít mé vlastní návratové objekty metody definované na ty, které klientovi řeknou přesně to, co poskytuje.

Následující fragmenty kódu ilustrují jednoduchý Film třída do značné míry generovaná NetBeans, kterou lze použít jako návratový typ spolu s kódem, který by mohl vrátit instanci dané třídy, spíše než obecnější a méně čitelnou datovou strukturu.

Movie.java

příklady zásilky; import java.util.Objects; / ** * Třída Simple Movie k prokázání toho, jak snadné je poskytnout více hodnot * v rámci jedné metody Java a vrátit čitelnost klientovi. * * @author Dustin * / public class Movie {private final String movieTitle; soukromý závěrečný int rok Vydáno; soukromý finální řetězec movieDirectorName; soukromá závěrečná smyčcový filmHodnocení; 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 () {návrat 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); vrátit 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 true; } @Override public String toString () {return "Movie {" + "movieTitle =" + movieTitle + ", yearReleased =" + yearReleased + ", movieDirectorName =" + movieDirectorName + ", movieRating =" + movieRating + '}'; }} 

Vrácení více podrobností v jednom objektu

 / ** * Poskytněte informace o filmu. * * @return Informace o filmu. * / public Movie getMovieInfo () {návrat nového filmu ("Oblivion", 2013, "Joseph Kosinski", "PG-13"); } 

Jednoduché psaní Film třída mi trvala asi 5 minut. Pomocí průvodce vytvořením třídy NetBeans jsem vybral název třídy a balíček a poté jsem zadal čtyři atributy třídy. Odtamtud jsem jednoduše použil mechanismus NetBeans „Insert Code“ k vložení přístupových metod „get“ spolu s přepsanými metodami toString (), hashCode () a equals (Object). Pokud jsem si nemyslel, že něco z toho potřebuji, mohl bych třídu udržet jednodušší, ale je opravdu snadné ji vytvořit tak, jak je. Nyní mám mnohem použitelnější návratový typ a to se odráží v kódu, který používá třídu. Nepotřebuje téměř tolik komentářů Javadocu k typu návratu, protože tento typ mluví sám za sebe a inzeruje svůj obsah pomocí metod „get“. Cítím, že malé množství dalšího úsilí k vytvoření těchto jednoduchých tříd pro vrácení více hodnot se vyplatí s obrovskými dividendami ve srovnání s alternativami, jako je návratový stav pomocí parametrů metody nebo použití obecnějších a těžší použití návratových datových struktur.

Není příliš překvapivé, že vlastní typ pro uložení více hodnot, které mají být vráceny volajícímu, je atraktivní řešení. Koneckonců, toto je koncepčně velmi podobné konceptům, které jsem blogoval o dříve souvisejících s používáním vlastních typů a parametrů objektů pro předávání více souvisejících parametrů, spíše než jejich předávání jednotlivě. Java je objektově orientovaný jazyk, a tak mě překvapuje, když nevidím objekty používané častěji v kódu Java pro organizaci parametrů a návratových hodnot v pěkném balíčku.

Výhody a výhody

Výhody použití objektů vlastních parametrů k reprezentaci a zapouzdření více návratových hodnot jsou zřejmé. Parametry metody mohou zůstat „vstupními“ parametry, protože ve vlastním objektu vráceném metodou lze poskytnout všechny výstupní informace (kromě informací o chybách komunikovaných prostřednictvím mechanismu výjimek). Jedná se o čistší přístup než používání obecných polí, sbírek, map, n-tic nebo jiných obecných datových struktur, protože všechny tyto alternativní přístupy přesouvají vývojové úsilí na všechny potenciální klienty.

Náklady a nevýhody

Vidím velmi malou nevýhodu psaní vlastních typů s více hodnotami, které se mají použít jako návratové typy z metod Java. Snad nejčastěji uváděnou cenou je cena psaní a testování těchto tříd, ale tato cena je docela malá, protože tyto třídy bývají jednoduché a protože moderní IDE za nás dělají většinu práce. Protože IDE to dělají automaticky, je kód obvykle správný. Třídy jsou tak jednoduché, že jsou snadno čitelné recenzenty kódu a lze je snadno otestovat.