Programavimas

Kodėl getter ir setter metodai yra blogis

Neketinau pradėti „yra blogis“ serijos, tačiau keli skaitytojai paprašė manęs paaiškinti, kodėl praėjusio mėnesio skiltyje „Kodėl praplečiama blogis“ minėjau, kad turėtumėte vengti „get / set“ metodų.

Nors „Java“ / „etter “metodai yra įprasti„ Java “, jie nėra ypač orientuoti į objektą (OO). Tiesą sakant, jie gali pakenkti jūsų kodo priežiūrai. Be to, daugybė „Getter“ ir „Setter“ metodų yra raudona vėliava, kad programa nebūtinai yra gerai suprojektuota iš OO perspektyvos.

Šiame straipsnyje paaiškinama, kodėl neturėtumėte naudoti getters ir seters (ir kada galite juos naudoti) ir siūloma dizaino metodika, kuri padės jums išsivaduoti iš getter / setter mentaliteto.

Dėl dizaino pobūdžio

Prieš patekdamas į kitą su dizainu susijusią skiltį (ne mažiau provokuojančiu pavadinimu), noriu patikslinti keletą dalykų.

Nustebau dėl kai kurių skaitytojų komentarų, gautų praėjusio mėnesio skiltyje „Kodėl praplečiama, yra blogis“ (žr. „Talkback“ paskutiniame straipsnio puslapyje). Kai kurie žmonės tikėjo, kad aš teigiau, jog objektų orientacija yra bloga vien dėl to tęsiasi turi problemų, tarsi abi sąvokos būtų lygiavertės. Aš tikrai ne tai pagalvojo Aš pasakiau, todėl leiskite man patikslinti kai kurias meta problemas.

Šis stulpelis ir praėjusio mėnesio straipsnis yra apie dizainą. Dizainas iš prigimties yra kompromisų serija. Kiekvienas pasirinkimas turi gerą ir blogą pusę, o jūs pasirenkate atsižvelgdamas į bendruosius kriterijus, apibrėžtus būtinybe. Gerai ir blogai vis dėlto nėra absoliutai. Geras sprendimas viename kontekste gali būti blogas kitame.

Jei nesuprantate abiejų klausimo pusių, negalite protingai pasirinkti; Tiesą sakant, jei jūs nesuprantate visų savo veiksmų pasekmių, jūs visiškai nesukuriate. Jūs suklupote tamsoje. Kiekvienas „Keturių gaujos“ skyrius nėra atsitiktinumas Dizaino modeliai knygoje yra skyrius „Pasekmės“, kuriame aprašoma, kada ir kodėl naudoti modelį yra netinkama.

Teigti, kad tam tikra kalbos ypatybė ar įprasta programavimo idioma (pvz., Prieigos priežiūrininkai) turi problemų, nėra tas pats, kas sakyti, kad niekada neturėtumėte jų naudoti jokiomis aplinkybėmis. Tai, kad dažniausiai naudojama savybė ar idioma, dar nereiškia jūsų turėtų naudokite ir tai. Neinformuoti programuotojai rašo daugybę programų ir paprasčiausiai „Sun Microsystems“ ar „Microsoft“ įdarbinimas magiškai nepagerina kažkieno programavimo ar projektavimo galimybių. „Java“ paketuose yra daug puikaus kodo. Bet yra ir to kodo dalių, esu tikra, kad autoriai drovisi pripažinti, kad parašė.

Tuo pačiu principu rinkodaros ar politinės paskatos dažnai skatina dizaino idėjas. Kartais programuotojai priima blogus sprendimus, tačiau įmonės nori reklamuoti tai, ką technologija gali padaryti, todėl jie pabrėžia, kad būdas, kuriuo jūs tai darote, yra ne toks idealus. Jie kuo geriau išnaudoja blogą situaciją. Vadinasi, jūs elgiatės neatsakingai, kai pritaikote bet kokią programavimo praktiką vien dėl to, kad „taip jūs turėtumėte daryti dalykus“. Daugelis nepavykusių „Enterprise JavaBeans“ (EJB) projektų įrodo šį principą. Tinkamai naudojama EJB technologija yra puiki technologija, tačiau netinkamai naudojama įmonė gali tiesiogine prasme nuversti įmonę.

Mano mintis yra ta, kad neturėtumėte programuoti aklai. Turite suprasti, kokį sumaištį ar idiomą gali sukelti. Tai darydami esate daug geresnėje padėtyje nuspręsdami, ar naudoti šią funkciją, ar idiomą. Jūsų pasirinkimas turėtų būti informuotas ir pragmatiškas. Šių straipsnių tikslas yra padėti jums atvirai žiūrėti į savo programavimą.

Duomenų abstrakcija

Pagrindinis OO sistemų priesakas yra tas, kad objektas neturėtų atskleisti jokių jo įgyvendinimo detalių. Tokiu būdu galite pakeisti įgyvendinimą nekeisdami kodo, kuris naudoja objektą. Iš to išplaukia, kad OO sistemose turėtumėte vengti „getter and setter“ funkcijų, nes jos dažniausiai suteikia prieigą prie įgyvendinimo detalių.

Norėdami sužinoti, kodėl, apsvarstykite, ar gali būti 1 000 skambučių į a getX () metodas jūsų programoje, o kiekvienas skambutis daro prielaidą, kad grąžinimo vertė yra tam tikro tipo. Galite laikyti getX ()Pavyzdžiui, vietinio kintamojo grąžinimo vertė ir to kintamojo tipas turi atitikti grąžinimo vertės tipą. Jei jums reikia pakeisti objekto įgyvendinimą taip, kad pasikeistų X tipas, jūs turite didelių problemų.

Jei X buvo an tarpt, bet dabar turi būti a ilgas, gausite 1000 kompiliavimo klaidų. Jei neteisingai išspręsite problemą, grąžindami grąžinimo vertę į tarpt, kodas bus kompiliuojamas švariai, bet jis neveiks. (Gali būti, kad grąžinimo vertė bus sutrumpinta.) Norėdami kompensuoti pokyčius, turite modifikuoti kiekvieną iš šių 1000 skambučių supantį kodą. Aš tikrai nenoriu dirbti tiek daug.

Vienas pagrindinių OO sistemų principų yra duomenų abstrakcija. Turėtumėte visiškai paslėpti, kaip objektas įgyvendina pranešimų tvarkytuvą, nuo likusios programos. Tai viena iš priežasčių, kodėl turėtų būti visi jūsų egzempliorių kintamieji (klasės nestandartiniai laukai) privatus.

Jei padarysite egzemplioriaus kintamąjį visuomenės, tada negalėsite pakeisti lauko, nes laikui bėgant klasė vystosi, nes sugadintumėte išorinį kodą, kuris naudoja šį lauką. Nenorite ieškoti 1000 klasės naudojimo būdų vien todėl, kad pakeisite tą klasę.

Šis diegimo slėpimo principas lemia gerą OO sistemos kokybės rūgštingumo testą: ar galite atlikti didžiulius klasės apibrėžimo pakeitimus - net išmesti visą daiktą ir pakeisti jį visiškai kitokiu įgyvendinimu - nepaveikdami to kodo, kuris naudoja tą klasės objektai? Toks moduliavimas yra pagrindinė objekto orientavimo prielaida ir žymiai palengvina priežiūrą. Neslepiant diegimo, nėra prasmės naudoti kitas OO funkcijas.

„Getter and setter“ metodai (dar vadinami „accessors“) yra pavojingi dėl tos pačios priežasties visuomenės laukai yra pavojingi: jie suteikia išorinę prieigą prie išsamios informacijos apie įgyvendinimą. Ką daryti, jei reikia pakeisti pasiekto lauko tipą? Taip pat turite pakeisti prieigos teikėjo grąžinimo tipą. Šią grąžinimo vertę naudojate daugelyje vietų, todėl taip pat turite pakeisti visą tą kodą. Noriu apriboti pakeitimo poveikį tik vienai klasės apibrėžčiai. Nenoriu, kad jie išplėstų į visą programą.

Kadangi prieigos elementai pažeidžia inkapsuliacijos principą, galite pagrįstai teigti, kad sistema, kuri labai ar netinkamai naudojasi prieigomis, tiesiog nėra orientuota į objektą. Jei atliksite projektavimo procesą, o ne tiesiog koduosite, jūsų programoje vargu ar rasite prieigų. Procesas yra svarbus. Turiu daugiau pasakyti šiuo klausimu straipsnio pabaigoje.

„Getter / setter“ metodų trūkumas nereiškia, kad kai kurie duomenys neteka sistemoje. Nepaisant to, geriausia kuo labiau sumažinti duomenų judėjimą. Mano patirtis rodo, kad išlaikomumas yra atvirkščiai proporcingas duomenų kiekiui, judančiam tarp objektų. Nors dar galite nematyti, kaip tai padaryti, galite iš tikrųjų pašalinti didžiąją dalį šio duomenų judėjimo.

Kruopščiai kurdami dizainą ir sutelkdami dėmesį į tai, ką turite padaryti, o ne į tai, kaip tai padarysite, pašalinsite didžiąją daugumą „Getter / Setter“ metodų savo programoje. Neprašykite informacijos, kurios jums reikia darbui atlikti; paprašykite objekto, kuriame yra informacijos, atlikti darbą už jus. Daugelis prieigų ieškančiųjų patenka į kodą, nes dizaineriai negalvojo apie dinaminį modelį: vykdymo laiko objektus ir pranešimus, kuriuos jie siunčia vienas kitam, kad atliktų darbą. Jie pradeda (neteisingai) kurdami klasių hierarchiją, o tada bando tas klases paminėti į dinaminį modelį. Šis požiūris niekada neveikia. Norėdami sukurti statinį modelį, turite atrasti ryšius tarp klasių, ir šie santykiai tiksliai atitinka pranešimų srautą. Asociacija tarp dviejų klasių egzistuoja tik tada, kai vienos klasės objektai siunčia pranešimus kitos klasės objektams. Pagrindinis statinio modelio tikslas yra užfiksuoti šią susiejimo informaciją dinamiškai modeliuojant.

Be aiškiai apibrėžto dinaminio modelio jūs tik spėliojate, kaip naudosite klasės objektus. Todėl prieigos metodai dažnai baigiasi modeliu, nes turite suteikti kuo daugiau prieigos, nes negalite numatyti, ar jums to reikės. Geriausia, kad tokia strategija pagal atspėjimą yra neefektyvi. Gaištate laiką rašydami nenaudingus metodus (arba pridedant nereikalingų galimybių į klases).

Aksesuarai taip pat baigiasi įpročio jėga. Kai procedūriniai programuotojai priima „Java“, jie pirmiausia linkę kurti žinomą kodą. Procesinėse kalbose nėra klasių, tačiau jos turi C struktūros (pagalvokite: klasė be metodų). Todėl atrodo natūralu imituoti a struktūros kuriant klasės apibrėžimus praktiškai be jokių metodų ir nieko kito visuomenės laukai. Šie procedūriniai programuotojai kažkur perskaitė, kad laukai turėtų būti privatus, tačiau taip jie daro laukus privatus ir tiekimas visuomenės prieigos metodai. Bet jie tik apsunkino visuomenės prieigą. Jie tikrai nepadarė sistemos objektu.

Nupiešk save

Vartotojo sąsajos (UI) konstrukcijoje yra vienas viso lauko inkapsuliacijos atskyrimas. Jei negalite naudoti prieigos priemonių, negalite turėti sąsajos kūrimo klasės skambučio a getAttribute () metodas. Vietoj to, klasėse yra panašių elementų Nupiešk save(...) metodai.

A „getIdentity“ () metodas taip pat gali veikti, žinoma, jei jis pateikia objektą, kuris įgyvendina Tapatybė sąsaja. Šioje sąsajoje turi būti: Nupiešk save() (arba duok man-a-JKomponentas- tai reiškia jūsų tapatybę) metodas. Nors „getIdentity“ prasideda „get“, tai nėra prieiga, nes ne tik grąžina lauką. Tai grąžina sudėtingą objektą, kurio elgesys pagrįstas. Net kai turiu Tapatybė objektu, aš vis dar neįsivaizduoju, kaip tapatybė yra reprezentuojama viduje.

Žinoma, a Nupiešk save() strategija reiškia, kad aš (aikteliu!) Įtraukiu vartotojo sąsajos kodą į verslo logiką. Apsvarstykite, kas nutiks, kai pasikeis vartotojo sąsajos reikalavimai. Tarkime, aš noriu atributą pavaizduoti visiškai kitaip. Šiandien „tapatybė“ yra vardas; rytoj tai vardas ir asmens kodas; kitą dieną po to tai vardas, asmens kodas ir paveikslėlis. Aš apriboju šių pakeitimų apimtį vienoje kodo vietoje. Jei aš turiu duoti manJKomponentas-tai atstovauja jūsų tapatybės klasei, tada aš išskyriau tapatybės vaizdavimo būdą nuo likusios sistemos.

Turėkite omenyje, kad iš tikrųjų neįdėjau jokio vartotojo sąsajos kodo į verslo logiką. Parašiau vartotojo sąsajos lygmenį AWT (Abstract Window Toolkit) arba „Swing“, kurie abu yra abstrakcijos sluoksniai. Tikrasis vartotojo sąsajos kodas yra AWT / „Swing“ diegime. Tai yra visa abstrakcijos sluoksnio esmė - norėdami izoliuoti savo verslo logiką nuo posistemio mechanikos. Aš galiu lengvai perkelti į kitą grafinę aplinką, nekeisdamas kodo, todėl vienintelė problema yra šiek tiek netvarkos. Šią netvarką galite lengvai pašalinti perkeldami visą vartotojo sąsajos kodą į vidinę klasę (arba naudodami fasado dizaino modelį).

„JavaBeans“

Galite paprieštarauti sakydami: „O kaip su„ JavaBeans “? Kas apie juos? Jūs tikrai galite kurti „JavaBeans“ be getters ir seterių. „BeanCustomizer“, „BeanInfo“ir „BeanDescriptor“ klasės egzistuoja būtent šiam tikslui. „JavaBean“ specialieji dizaineriai į paveikslėlį įmetė „getter“ / „seterio“ idiomą, nes manė, kad tai bus paprastas būdas greitai paruošti pupelę - ką galite padaryti mokydamiesi, kaip tai padaryti teisingai. Deja, to niekas nedarė.

Prieigos priemonės buvo sukurtos tik kaip būdas pažymėti tam tikras ypatybes, kad vartotojo sąsajos kūrimo programa ar lygiavertė priemonė galėtų jas identifikuoti. Jūs neturėtumėte patys vadinti šių metodų. Jie egzistuoja tam, kad būtų galima naudoti automatizuotą įrankį. Šis įrankis naudoja introspection API Klasė klasę rasti metodus ir ekstrapoliuoti tam tikrų savybių egzistavimą iš metodų pavadinimų. Praktiškai ši savistaba pagrįsta idioma nepasiteisino. Tai padarė kodą labai sudėtingą ir procedūrinį. Programuotojai, kurie nesupranta duomenų abstrakcijos, iš tikrųjų paskambina prieigoms, todėl kodas yra mažiau prižiūrimas. Dėl šios priežasties metaduomenų funkcija bus integruota į „Java 1.5“ (numatoma 2004 m. Viduryje). Taigi vietoj:

privati ​​int nuosavybė; public int getProperty () {return property; } public void setProperty (int vertė} {nuosavybė = vertė;} 

Galėsite naudoti kažką panašaus:

privatus @ nuosavybės int nuosavybė; 

Vartotojo sąsajos kūrimo įrankis arba lygiavertis naudos savitikslės API savybėms rasti, o ne nagrinės metodų pavadinimus ir iš pavadinimo padarys išvadą apie nuosavybės egzistavimą. Todėl joks vykdymo laiko prieigos elementas nepažeidžia jūsų kodo.

Kada tvarkosi aksesuaras?

Pirma, kaip jau aptariau anksčiau, tinka metodas grąžinti objektą sąsajos požiūriu, kurią objektas įgyvendina, nes ta sąsaja izoliuoja jus nuo diegimo klasės pakeitimų. Toks metodas (kuris pateikia sąsajos nuorodą) nėra iš tikrųjų „getter“ ta prasme, kad tik suteikiama prieiga prie lauko. Jei pakeisite vidinį teikėjo diegimą, tiesiog pakeiskite grąžinto objekto apibrėžimą, kad jis atitiktų pakeitimus. Jūs vis tiek saugote išorinį kodą, kuris naudoja objektą per jo sąsają.