Programovanie

Prečo sú metódy getra a setra zlé

Nemal som v úmysle zahájiť sériu „je zlo“, ale niekoľko čitateľov ma požiadalo, aby som vysvetlil, prečo som v minulomesačnom stĺpci „Why extends Is Evil“ spomenul, že by ste sa mali vyhnúť metódam get / set.

Aj keď sú metódy getter / setter v Jave bežné, nie sú nijako zvlášť objektovo orientované (OO). V skutočnosti môžu poškodiť udržiavateľnosť vášho kódu. Prítomnosť mnohých metód getra a setra je navyše červenou vlajkou, že program nemusí byť z pohľadu OO nevyhnutne dobre navrhnutý.

Tento článok vysvetľuje, prečo by ste nemali používať getry a settery (a kedy ich môžete použiť) a navrhuje metodiku návrhu, ktorá vám pomôže vymaniť sa z mentality getra / setra.

O povahe dizajnu

Predtým, ako sa pustím do iného stĺpca súvisiaceho s dizajnom (nie menej s provokatívnym názvom), chcem si ujasniť niekoľko vecí.

Ohromili ma niektoré komentáre čitateľov, ktoré vyplynuli z minulomesačného stĺpca „Why extends Is Evil“ (pozri Talkback na poslednej stránke článku). Niektorí ľudia verili, že som tvrdil, že objektová orientácia je zlá jednoducho preto, lebo predlžuje má problémy, akoby si tieto dva pojmy boli rovnocenné. To určite nie je to, čo ja pomyslel si Povedal som, takže mi dovoľte objasniť niektoré meta-problémy.

Tento stĺpec a článok z minulého mesiaca je o dizajne. Dizajn je svojou podstatou radom kompromisov. Každá voľba má dobrú aj zlú stránku a vy sa rozhodujete v kontexte všeobecných kritérií definovaných nevyhnutnosťou. Dobré a zlé však nie sú absolútne. Dobré rozhodnutie v jednom kontexte môže byť zlé v inom.

Ak nerozumiete obom stránkam problému, nemôžete sa rozhodnúť inteligentne; v skutočnosti, ak nerozumieš všetkým dôsledkom svojich činov, vôbec nenavrhuješ. Klopýtate v tme. Nie je náhodou, že každá kapitola v Gangu štyroch Dizajnové vzory Kniha obsahuje časť „Dôsledky“, ktorá popisuje, kedy a prečo je použitie vzoru nevhodné.

Tvrdenie, že niektorá jazyková funkcia alebo bežný programovací idiom (napríklad prístupový program) má problémy, nie je to isté ako tvrdenie, že by ste ich za žiadnych okolností nemali nikdy používať. To, že sa funkcia alebo idióm bežne používa, ešte neznamená, že vás by mal buď to použi. Neinformovaní programátori píšu veľa programov a ich prosté zamestnanie v Sun Microsystems alebo Microsoftu nijako zázračne nezlepšuje niekoho programovacie ani dizajnérske schopnosti. Balíky Java obsahujú veľa skvelého kódu. Ale sú tu aj časti tohto kódu, som si istý, že autori sa hanbia priznať, že napísali.

Rovnako aj marketingové alebo politické stimuly často tlačia na návrhové idiómy. Programátori niekedy robia zlé rozhodnutia, ale spoločnosti chcú propagovať, čo dokáže táto technológia, a tak zdôrazňujú, že spôsob, akým to robíte, je menej ako ideálny. Zo zlej situácie vyťažia maximum. Preto, keď si osvojíte akýkoľvek programátorský postup, budete konať nezodpovedne iba preto, že „tak máte robiť veci“. Mnoho zlyhaných projektov Enterprise JavaBeans (EJB) dokazuje tento princíp. Technológia založená na EJB je vynikajúcou technológiou pri správnom použití, ale pri nevhodnom použití môže spoločnosť doslova zničiť.

Ide mi o to, že by ste nemali programovať naslepo. Musíte pochopiť, akú katastrofu môže spôsobiť určitá funkcia alebo idiom. Pritom máte oveľa lepšiu pozíciu pri rozhodovaní, či by ste mali použiť túto funkciu alebo frázu. Vaše rozhodnutia by mali byť informované a pragmatické. Účelom týchto článkov je pomôcť vám v prístupe k programovaniu s otvorenými očami.

Abstrakcia dát

Základnou požiadavkou systémov OO je, že objekt by nemal vystavovať žiadne podrobnosti implementácie. Týmto spôsobom môžete zmeniť implementáciu bez zmeny kódu, ktorý používa objekt. Z toho vyplýva, že v systémoch OO by ste sa mali vyhnúť funkciám getter a setter, pretože väčšinou poskytujú prístup k podrobnostiam implementácie.

Ak chcete zistiť dôvod, zvážte, že by mohlo byť 1 000 hovorov na a getX () vo vašom programe a každé volanie predpokladá, že návratová hodnota je konkrétneho typu. Môžete uložiť getX ()Napríklad návratová hodnota v lokálnej premennej a tento typ premennej sa musí zhodovať s návratovou hodnotou. Ak potrebujete zmeniť spôsob implementácie objektu takým spôsobom, že sa zmení typ X, máte veľké problémy.

Keby X bol int, ale teraz musí byť dlho, dostanete 1 000 chýb pri kompilácii. Ak problém opravíte nesprávne, vrátením hodnoty vrátite hodnotu int, kód sa skompiluje čisto, ale nebude to fungovať. (Vrátená hodnota môže byť skrátená.) Musíte kompenzovať zmenu, ktorá obklopuje každé z týchto 1 000 hovorov. Určite nechcem robiť toľko práce.

Jedným zo základných princípov OO systémov je abstrakcia dát. Pred zvyškom programu by ste mali úplne skryť spôsob, ktorým objekt implementuje obslužnú rutinu správy. To je jeden z dôvodov, prečo by mali byť všetky vaše inštančné premenné (nekonštantné polia triedy) súkromné.

Ak urobíte inštančnú premennú verejné, potom nemôžete zmeniť pole, pretože trieda sa časom vyvíja, pretože by ste porušili externý kód, ktorý pole používa. Nechcete hľadať 1 000 použití triedy len preto, že túto triedu zmeníte.

Tento princíp skrývania implementácie vedie k dobrému testu kvality systému OO: Môžete vykonať rozsiahle zmeny v definícii triedy - dokonca vyhodiť celú vec a nahradiť ju úplne inou implementáciou - bez toho, aby to malo dopad na akýkoľvek kód, ktorý ju používa predmety triedy? Tento druh modularizácie je ústredným predpokladom objektovej orientácie a výrazne uľahčuje údržbu. Bez skrytia implementácie nemá zmysel používať ďalšie funkcie OO.

Metódy getra a setra (známe tiež ako prístupové mechanizmy) sú nebezpečné z rovnakého dôvodu verejné polia sú nebezpečné: Poskytujú externý prístup k podrobnostiam implementácie. Čo ak potrebujete zmeniť typ prístupného poľa? Musíte tiež zmeniť návratový typ prístupu. Túto návratovú hodnotu použijete na mnohých miestach, takže musíte tiež zmeniť celý tento kód. Chcem obmedziť účinky zmeny na definíciu jednej triedy. Nechcem, aby sa vlnili do celého programu.

Pretože prístupoví pracovníci porušujú princíp zapuzdrenia, môžete dôvodne tvrdiť, že systém, ktorý prístupové zariadenia veľmi alebo nevhodne používa, jednoducho nie je objektovo orientovaný. Ak prejdete procesom návrhu, na rozdiel od jednoduchého programovania, vo svojom programe nenájdete takmer žiadneho prístupu. Proces je dôležitý. K tejto otázke chcem povedať viac na konci článku.

Nedostatok metód getter / setter neznamená, že niektoré údaje neprúdia systémom. Najlepšie je napriek tomu pohyb údajov čo najviac minimalizovať. Moje skúsenosti sú také, že udržiavateľnosť je nepriamo úmerná množstvu údajov, ktoré sa pohybujú medzi objektmi. Aj keď možno ešte nevidíte, ako je to možné, väčšinu tohto pohybu údajov môžete v skutočnosti vylúčiť.

Dôsledným navrhovaním a zameraním na to, čo musíte robiť, a nie na to, ako to urobíte, eliminujete drvivú väčšinu metód getra / setra vo vašom programe. Nepýtajte si informácie, ktoré potrebujete na vykonanie práce; požiadajte objekt, ktorý má informácie, aby vykonal prácu za vás. Väčšina prístupových osôb si nájde cestu do kódu, pretože dizajnéri nemysleli na dynamický model: runtime objekty a správy, ktoré si navzájom posielajú, aby vykonali prácu. Začínajú (nesprávne) návrhom hierarchie tried a potom sa snažia tieto triedy obuť do dynamického modelu. Tento prístup nikdy nefunguje. Ak chcete vytvoriť statický model, musíte zistiť vzťahy medzi triedami a tieto vzťahy presne zodpovedajú toku správ. Asociácia existuje medzi dvoma triedami iba vtedy, keď objekty jednej triedy posielajú správy objektom druhej triedy. Hlavným účelom statického modelu je zachytiť tieto informácie o asociácii pri dynamickom modelovaní.

Bez jasne definovaného dynamického modelu iba hádate, ako použijete objekty triedy. V dôsledku toho sa metódy prístupových modelov často končia v modeli, pretože musíte poskytnúť čo najväčší prístup, pretože nemôžete predvídať, či ich budete alebo nebudete potrebovať. Tento druh stratégie návrhu podľa hádania je v najlepšom prípade neefektívny. Strácate čas písaním zbytočných metód (alebo pridávaním zbytočných schopností do tried).

Prídavné zariadenia tiež končia v dizajnoch silou zvyku. Keď procedurálni programátori adoptujú Javu, majú tendenciu začať budovaním známeho kódu. Procedurálne jazyky nemajú triedy, ale majú C štruktúr (premýšľajte: trieda bez metód). Zdá sa teda prirodzené napodobňovať a štruktúr vytváraním definícií tried prakticky bez metód a nič iného ako verejné polia. Títo procedurálni programátori niekde čítajú, že polia by mali byť súkromné, tak však robia polia súkromné a zásobovanie verejné prístupové metódy. Lenže iba skomplikovali prístup verejnosti. Systém určite neurobili objektovo orientovaným.

Nakresli sa

Jedným z dôsledkov zapuzdrenia celého poľa je konštrukcia používateľského rozhrania (UI). Ak nemôžete použiť prístupové zariadenia, nemôžete mať volanie triedy tvorcu používateľského rozhrania a getAttribute () metóda. Namiesto toho majú triedy prvky ako drawYourself (...) metódy.

A getIdentity () metóda môže samozrejme tiež fungovať, ak vráti objekt, ktorý implementuje Identita rozhranie. Toto rozhranie musí obsahovať a drawYourself () (alebo daj miJComponent-to-predstavuje-vašu-identitu) metóda. Predsa getIdentity začína na „get“, nejde o prístupového objektu, pretože nielen vracia pole. Vráti zložitý objekt, ktorý sa vyznačuje primeraným správaním. Aj ked mam Identita objekt, stále netuším, ako je identita predstavovaná vnútorne.

Samozrejme, a drawYourself () stratégia znamená, že som (lapal po dychu) vložil kód UI do obchodnej logiky. Zvážte, čo sa stane, keď sa zmenia požiadavky používateľského rozhrania. Povedzme, že chcem reprezentovať atribút úplne iným spôsobom. Dnes je „identita“ meno; zajtra je to meno a IČO; nasledujúci deň je to meno, identifikačné číslo a obrázok. Rozsah týchto zmien obmedzujem na jedno miesto v kóde. Ak mám daj miJComponent-táto trieda-predstavuje-tvoju identitu, potom som izoloval spôsob, akým sú identity reprezentované od zvyšku systému.

Majte na pamäti, že som do obchodnej logiky vlastne nevložil žiadny kód používateľského rozhrania. Vrstvu UI som napísal v zmysle AWT (Abstract Window Toolkit) alebo Swing, čo sú obe abstrakčné vrstvy. Aktuálny kód používateľského rozhrania je v implementácii AWT / Swing. To je celá podstata abstrakčnej vrstvy - izolovať vašu obchodnú logiku od mechaniky subsystému. Ľahko sa môžem preniesť do iného grafického prostredia bez zmeny kódu, takže jediným problémom je malý neporiadok. Tento neporiadok môžete ľahko eliminovať presunutím celého kódu používateľského rozhrania do vnútornej triedy (alebo použitím návrhového vzoru Fasáda).

JavaBeans

Môžete namietať slovami: „Ale čo JavaBeans?“ Čo s nimi? Určite si môžete zostaviť JavaBeans bez getrov a setterov. The BeanCustomizer, BeanInfoa BeanDescriptor triedy existujú presne na tento účel. Konštruktéri špecializujúci sa na JavaBean vrhli do obrazu frázu getter / setter, pretože si mysleli, že by to bol ľahký spôsob, ako rýchlo vyrobiť fazuľu - niečo, čo môžete robiť, kým sa naučíte, ako to robiť dobre. Bohužiaľ to nikto neurobil.

Prístupové objekty boli vytvorené iba ako spôsob označovania určitých vlastností, aby ich mohol identifikovať program na vytváranie používateľských rozhraní alebo ekvivalentný program. Nemali by ste tieto metódy nazývať sami. Existujú na použitie automatizovaným nástrojom. Tento nástroj používa introspekčné API v Trieda triedy na vyhľadanie metód a extrapoláciu existencie určitých vlastností z názvov metód. V praxi tento fráza založená na introspekcii nevyšla. Tento kód je nesmierne komplikovaný a procedurálny. Programátori, ktorí nechápu abstrakciu údajov, skutočne volajú prístupové subjekty a v dôsledku toho je kód menej udržiavateľný. Z tohto dôvodu bude funkcia metadát začlenená do Java 1.5 (má byť k dispozícii v polovici roku 2004). Takže namiesto:

súkromné ​​int vlastníctvo; public int getProperty () {return property; } public void setProperty (int hodnota} {property = value;} 

Budete môcť použiť niečo ako:

private @property int property; 

Nástroj na vytváranie používateľského rozhrania alebo jeho ekvivalent použije introspekčné rozhrania API na nájdenie vlastností namiesto toho, aby preskúmal názvy metód a odvodil existenciu vlastnosti z názvu. Žiadny runtime modul preto nepoškodzuje váš kód.

Kedy je prístupový bod v poriadku?

Po prvé, ako som už skôr uviedol, je v poriadku, ak metóda vracia objekt z hľadiska rozhrania, ktoré objekt implementuje, pretože dané rozhranie vás izoluje od zmien implementujúcej triedy. Tento druh metódy (ktorá vracia referenciu na rozhranie) nie je v skutočnosti „getterom“ v zmysle metódy, ktorá iba poskytuje prístup k poľu. Ak zmeníte internú implementáciu poskytovateľa, stačí zmeniť definíciu vráteného objektu, aby sa zmeny mohli prispôsobiť. Stále chránite externý kód, ktorý používa objekt prostredníctvom jeho rozhrania.

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