Ta mesec obrok za Tehnike oblikovanja je drugi v mini seriji stolpcev o oblikovanju predmetov. V stolpcu prejšnjega meseca, ki je zajemal oblikovanje objektov za pravilno inicializacijo, sem govoril o tem, kako oblikovati konstruktorje in inicializatorje. Ta mesec in naslednji mesec bom razpravljal o načelih oblikovanja dejanskih področij in metod predavanja. Po tem bom pisal o zaključnikih in pokazal, kako oblikovati predmete za pravilno čiščenje na koncu njihovega življenja.
Gradivo za ta članek (izogibanje posebnim vrednostim podatkov, uporaba konstant, zmanjšanje povezovanja) in naslednji članek (maksimiranje kohezije) je morda znano številnim bralcem, saj gradivo temelji na splošnih načelih oblikovanja, ki so precej neodvisna od programskega jezika Java . Ker pa sem se v preteklih letih srečal s toliko kode, ki teh načel ne izkorišča, mislim, da si zaslužijo občasno ponovitev. Poleg tega v tem članku poskušam pokazati, kako ta splošna načela veljajo zlasti za jezik Java.
Oblikovanje polj
Pri načrtovanju polj je glavno pravilo izogibanje uporabi ene spremenljivke za predstavitev več atributov razreda. To pravilo lahko kršite tako, da v spremenljivki označite posebne vrednosti, od katerih ima vsaka svoj poseben pomen.
Kot se uporablja tukaj, atribut je značilnost predmeta ali razreda. Dva atributa a Kavna skodelica
predmet, na primer, je lahko:
- Količina kave, ki jo vsebuje skodelica
- Ali je skodelica čista ali umazana
Če si želite natančneje ogledati to pravilo, si predstavljajte, da načrtujete a Kavna skodelica
razred za virtualno kavarno, opisano v prejšnjem mesecu Tehnike oblikovanja stolpec. Predpostavimo, da želite modelirati, ali je bila skodelica za kavo v vaši virtualni kavarni oprana in je pripravljena za uporabo s strani naslednje stranke. S temi informacijami lahko zagotovite, da kavne skodelice ne boste več uporabili, preden jo operete.
Če se odločite, da vas zanima samo, ali je bila skodelica oprana, če je prazna, lahko uporabite posebno vrednost innerCoffee
polje, ki se običajno uporablja za beleženje količine kave v skodelici, ki predstavlja neoprano skodelico. Če je največja količina kave v vaši največji skodelici 473 mililitrov (16 unč tekočine), potem je največja innerCoffee
običajno bi bilo 473. Tako lahko uporabite innerCoffee
vrednost, recimo 500 (posebna vrednost), ki označuje prazno skodelico, ki je neoprana:
// V izvornem paketu v datotečnih poljih / ex1 / CoffeeCup.java razred CoffeeCup {private int innerCoffee; public boolean isReadyForNextUse () {// Če skodelica za kavo ni oprana, potem // ni pripravljena za naslednjo uporabo if (innerCoffee == 500) {return false; } vrni true; } javna void setCustomerDone () {innerCoffee = 500; // ...} javno prazno prazno () {innerCoffee = 0; // ...} // ...}
Ta koda bo dala Kavna skodelica
ugovarja želenemu vedenju. Težava tega pristopa je, da posebne vrednosti niso zlahka razumljive in otežujejo spreminjanje kode. Tudi če v komentarju opišete posebne vrednosti, bo morda drugim programerjem treba več časa, da bodo razumeli, kaj počne vaša koda. Poleg tega morda nikoli ne bodo razumeli vaše kode. Lahko nepravilno uporabljajo vaš razred ali ga spremenijo tako, da uvedejo napako.
Če na primer kasneje nekdo ponudbi virtualne kavarne doda skodelico 20 unč, bi bilo v skodelico mogoče shraniti do 592 mililitrov (ml) kave. Če programer doda novo velikost skodelice, ne da bi se zavedal, da s 500 ml označujete, da je treba skodelico oprati, bo verjetno prišlo do napake. Če bi stranka v vaši virtualni kavarni kupila skodelico 20 unč in nato zaužila velik 92-mililitrski koktajl, bi ji v skodelici ostalo natanko 500 ml. Kupec bi bil šokiran in nezadovoljen, ko bi mu po zaužitju le 92 ml skodelica izginila iz roke in se pojavila v umivalniku, pripravljena za pranje. Tudi, če bi programer, ki je izvedel spremembo, ugotovil, da uporabljate posebno vrednost, bi bilo treba izbrati drugo posebno vrednost za neoprani atribut.
Boljši pristop k tej situaciji je ločeno polje za modeliranje ločenega atributa:
// V izvornem paketu v datotečnih poljih / ex2 / CoffeeCup.java razred CoffeeCup {private int innerCoffee; zasebno logično potrebe za pranje; public boolean isReadyForNextUse () {// Če skodelica za kavo ni oprana, potem // ni pripravljena za naslednjo uporabo return! needsWashing; } javna void setCustomerDone () {needsWashing = true; // ...} javno prazno pranje () {needsWashing = false; // ...} // ...}
Tukaj innerCoffee
polje se uporablja samo za modeliranje količine kave v atributu skodelice. Atribut pomivanja skodelice je modeliran z potrebe pranja
polje. Ta shema je lažje razumljiva kot prejšnja shema, ki je uporabljala posebno vrednost innerCoffee
in ne bi preprečil, da bi nekdo razširil največjo vrednost za innerCoffee
.
Uporaba konstant
Drugo pravilo, ki ga je treba upoštevati pri ustvarjanju polj, je uporaba konstant (statičnih končnih spremenljivk) za konstantne vrednosti, ki se posredujejo, vrnejo ali uporabijo znotraj metod. Če metoda v enem od svojih parametrov pričakuje eno od končnih množic konstantnih vrednosti, definiranje konstant pripomore k temu, da je odjemalskim programerjem bolj jasno, kaj je treba posredovati v tem parametru. Če metoda vrne eno od končnih množic vrednosti, razglasitev konstant odjemalskim programerjem postane bolj očitno, kaj lahko pričakujejo kot rezultat. To je na primer lažje razumeti:
če (cup.getSize () == CoffeeCup.TALL) {}
kot je to razumeti:
če (cup.getSize () == 1) {}
Prav tako bi morali določiti konstante za interno uporabo z metodami razreda - tudi če se te konstante ne uporabljajo zunaj razreda - zato jih je lažje razumeti in spremeniti. Uporaba konstant naredi kodo bolj prilagodljivo. Če se zavedate, da ste napačno izračunali vrednost in niste uporabili konstante, boste morali pregledati kodo in spremeniti vsak pojav trdo kodirane vrednosti. Če pa ste uporabili konstanto, jo boste morali spremeniti le tam, kjer je definirana kot konstanta.
Konstante in prevajalnik Java
Koristno je vedeti o prevajalniku Java, da statična končna polja (konstante) obravnava drugače kot druge vrste polj. Sklici na statične končne spremenljivke, inicializirane v konstanto časa prevajanja, se v času prevajanja razrešijo na lokalno kopijo vrednosti konstante. To velja za konstante vseh primitivnih tipov in vrst java.lang.String
.
Običajno se vaš razred nanaša na drug razred - recimo na razred java.lang.Math
- prevajalnik Java postavi simbolična sklicevanja na razred Matematika
v datoteko predavanja za vaš razred. Na primer, če se prikliče metoda vašega razreda Math.sin ()
, bo datoteka predavanja vsebovala dve simbolni sklici na Matematika
:
- Simbolično sklicevanje na razred
Matematika
- Simbolično sklicevanje na
Matematika
jegreh ()
metoda
Za izvedbo kode v vašem razredu, ki se nanaša na Math.sin ()
, bi moral JVM naložiti razred Matematika
za razrešitev simboličnih sklicev.
Če se je po drugi strani vaša koda nanašala samo na statično spremenljivko končnega razreda PI
razglašen v razredu Matematika
, prevajalnik Java ne bi dal nobene simbolične reference na Matematika
v datoteki predavanja za vaš razred. Namesto tega bi preprosto postavil kopijo dobesedne vrednosti Math.PI
v datoteko razreda vašega predavanja. Za izvedbo kode v razredu, ki uporablja Math.PI
konstantno, JVM ne bi bilo treba naložiti razreda Matematika
.
Rezultat te funkcije prevajalnika Java je, da JVM ni treba bolj trdo delati, da bi uporabljal konstante kot da uporablja literale. Prednost konstantam pred dobesednimi je ena redkih smernic za načrtovanje, ki povečuje prožnost programa, ne da bi pri tem tvegala poslabšanje zmogljivosti programa.
Tri vrste metod
Preostanek tega članka bo obravnaval tehnike načrtovanja metode, ki se nanašajo na podatke, ki jih metoda uporablja ali spreminja. V tem kontekstu bi rad opredelil in poimenoval tri osnovne vrste metod v programih Java: uporabna metoda metoda stanja stanja, in metoda spremembe stanja.
Uporabna metoda
Uporabna metoda je metoda razreda, ki ne uporablja ali spreminja stanja (spremenljivke razreda) svojega razreda. Ta vrsta metode preprosto nudi koristno storitev, povezano s svojim razredom predmeta.
Nekaj primerov uporabnih metod iz Java API je:
- (V razredu
Celo število
)javni statični int toString (int i)
- vrne novoVrvica
objekt, ki predstavlja določeno celo število v radiksu 10 - (V razredu
Matematika
)javni statični nativni dvojni cos (dvojni a)
- vrne trigonometrični kosinus kota
Metoda stanja stanja
Metoda pogleda stanja je metoda razreda ali primerka, ki vrne nekaj pogleda na notranje stanje razreda ali predmeta, ne da bi to stanje spremenila. (Ta vrsta metode drzno ne upošteva Heisenbergovega načela negotovosti - glejte Vire, če potrebujete osvežitev po tem principu.) Metoda pogleda stanja lahko preprosto vrne vrednost spremenljivke razreda ali primerka ali pa vrne vrednost, izračunano iz več spremenljivk razreda ali primerka.
Nekaj primerov metod pogleda stanja iz API-ja Java je:
- (V razredu
Predmet
)javni String toString ()
- vrne nizovno predstavitev predmeta - (V razredu
Celo število
)javni bajt byteValue ()
- vrne vrednostCelo število
objekt kot bajt - (V razredu
Vrvica
)javni int indeksOf (int ch)
- vrne indeks v nizu prvega pojavljanja določenega znaka
Metoda spremembe stanja
Metoda spremembe stanja je metoda, ki lahko preoblikuje stanje razreda, v katerem je metoda deklarirana, ali, če je metoda primerka, objekt, na katerega se prikliče. Ko se prikliče metoda spremembe stanja, predstavlja "dogodek" za razred ali predmet. Koda metode "obdeluje" dogodek, kar lahko spremeni stanje razreda ali predmeta.
Nekaj primerov metod spreminjanja stanja iz Java API je:
- (V razredu
StringBuffer
)javni dodatek StringBuffer (int i)
- doda nizno predstavitevint
argument zaStringBuffer
- (V razredu
Hashtable
)javna sinhronizirana void clear ()
- počistiHashtable
tako da ne vsebuje ključev - (V razredu
Vektor
)javni končni sinhronizirani void addElement (objekt obj)
- doda določeno komponento na konecVektor
, povečala svojo velikost za eno
Zmanjšanje povezovanja metode
Oboroženi s temi opredelitvami metod uporabnosti, stanja in sprememb stanja ste pripravljeni na razpravo o spenjanju metod.
Med načrtovanjem metod bi moral biti eden od vaših ciljev minimalizirati sklopka - stopnja medsebojne odvisnosti metode in njenega okolja (druge metode, predmeti in razredi). Manj je povezav med metodo in okoljem, bolj neodvisna je ta metoda in bolj prilagodljiva je zasnova.
Metode kot podatkovni transformatorji
Da bi razumeli spenjanje, pomaga razmišljati o metodah zgolj kot o pretvornikih podatkov. Metode sprejemajo podatke kot vhodne podatke, izvajajo operacije nad njimi in generirajo podatke kot izhodne podatke. Stopnja povezanosti metode je odvisna predvsem od tega, kje dobi vhodne podatke in kam postavi izhodne podatke.
Slika 1 prikazuje grafični prikaz metode kot podatkovnega transformatorja: Diagram toka podatkov iz strukturirane (ne objektno usmerjene) zasnove.
Vhod in izhod
Metoda v Javi lahko dobi vhodne podatke iz številnih virov:
- Od klicatelja lahko zahteva, da ob vpoklicu navede svoje vhodne podatke kot parametre
- Zajema lahko podatke iz vseh dostopnih spremenljivk razreda, kot so lastne spremenljivke razreda ali katere koli dostopne spremenljivke razreda drugega razreda
- Če gre za metodo primerka, lahko zagrabi spremenljivke primerka iz predmeta, na katerem je bil poklican
Metoda lahko izrazi svoje rezultate na mnogih mestih:
- Vrne lahko vrednost, bodisi primitivni tip bodisi sklic na objekt
- Lahko spremeni predmete, na katere se sklicujejo reference, posredovane kot parametri
- Spremeni lahko katero koli spremenljivko razreda svojega razreda ali katero koli dostopno spremenljivko razreda drugega razreda
- Če gre za metodo primerka, lahko spremeni vse spremenljivke primerka predmeta, na katerem je bil poklican
- Lahko vrže izjemo
Upoštevajte, da parametri, vrnjene vrednosti in vržene izjeme niso edine vrste vhodov in izhodov metode, omenjene na zgornjih seznamih. Spremenljivke primerka in razreda se prav tako obravnavajo kot vhod in izhod. Z objektno usmerjenega vidika se to morda zdi neintuitivno, ker je dostop do spremenljivk primerka in razreda v Javi "samodejen" (metodi ni treba izrecno ničesar posredovati). Pri poskusu merjenja povezave metode pa morate pogledati vrsto in količino podatkov, ki jih koda uporablja in spreminja, ne glede na to, ali je bil dostop kode do teh podatkov "samodejen".
Minimalno povezane uporabne metode
Najmanj povezana metoda, ki je možna v Javi, je uporabna metoda, ki:
- Vnese samo iz svojih parametrov
- Izrazi svoje rezultate samo s svojimi parametri ali vrnjeno vrednostjo (ali z vrnitvijo izjeme)
- Kot vhod sprejema samo podatke, ki jih metoda dejansko potrebuje
- Vrne kot izhod samo podatke, ki jih metoda dejansko ustvari
Dobra uporabna metoda
Na primer metoda convertOzToMl ()
prikazano spodaj sprejema int
kot edini vhod in vrne int
kot edini rezultat: