Tämän kuukauden erä Suunnittelutekniikat on toinen esineiden suunnittelua käsittelevästä sarakkeesta. Viime kuun sarakkeessa, joka käsitteli esineiden suunnittelua asianmukaista alustamista varten, puhuin rakentajien ja alustajien suunnittelusta. Tässä kuussa ja ensi kuussa keskustelen luokan varsinaisten kenttien ja menetelmien suunnitteluperiaatteista. Sen jälkeen kirjoitan viimeistelijöistä ja näytän, kuinka suunnitella esineitä kunnollista puhdistusta varten heidän elämänsä lopussa.
Tämän artikkelin materiaali (erityisten data-arvojen välttäminen, vakioiden käyttö, kytkennän minimointi) ja seuraava artikkeli (kohimoinnin maksimointi) ja seuraava artikkeli (maksimoivat yhteenkuuluvuuden) voivat olla tuttuja lukijoille, koska materiaali perustuu yleisiin suunnitteluperiaatteisiin, jotka ovat melko riippumattomia Java-ohjelmointikielestä . Koska olen kuitenkin kohdannut vuosien varrella niin paljon koodeja, joissa ei hyödynnetä näitä periaatteita, mielestäni ne ansaitsevat ajoittain uudelleen. Lisäksi yritän tässä artikkelissa osoittaa, kuinka nämä yleiset periaatteet koskevat erityisesti Java-kieltä.
Kenttien suunnittelu
Kenttien suunnittelussa tärkein nyrkkisääntö on välttää yhden muuttujan käyttämistä luokan useiden attribuuttien edustamiseen. Voit rikkoa tätä sääntöä merkitsemällä muuttujan erityisarvot, joilla jokaisella on oma erityinen merkityksensä.
Tässä käytettynä määritteen on kohteen tai luokan erottava ominaisuus. Kaksi a-attribuuttia Kahvikuppi
esine voi esimerkiksi olla:
- Kupin sisältämän kahvin määrä
- Onko kuppi puhdas vai likainen
Tarkastele tätä sääntöä tarkemmin kuvittelemalla, että suunnittelet Kahvikuppi
luokka viime kuussa kuvatulle virtuaalikahvilalle Suunnittelutekniikat sarake. Oletetaan, että haluat mallintaa, onko virtuaalikahvilasi kahvikuppi pesty ja onko seuraava asiakas valmis sitä käyttämään. Näiden tietojen avulla voit varmistaa, että et käytä kahvikuppia uudelleen ennen kuin se on pesty.
Jos päätät, että välität vain siitä, onko kuppi pesty, jos se on tyhjä, voit käyttää kupin erityisarvoa sisäinen kahvi
kenttä, jota tavallisesti käytetään pitämään kirjaa kupin kahvimäärästä edustamaan pesemätöntä kuppia. Jos 473 millilitraa (16 nesteunssia) on suurin kahvimäärä suurimmassa kupillessasi, sisäinen kahvi
normaalisti olisi 473. Voit siis käyttää sisäinen kahvi
arvo esimerkiksi 500 (erikoisarvo) ilmaisemaan tyhjää kuppia, joka on pesemätön:
// Lähdepaketissa tiedostokentissä / ex1 / CoffeeCup.java class CoffeeCup {private int innerCoffee; public boolean isReadyForNextUse () {// Jos kahvikuppi ei ole pesty, se // ei ole valmis seuraavaa käyttöä varten, jos (innerCoffee == 500) {return false; } return true; } public void setCustomerDone () {innerCoffee = 500; // ...} public void wash () {innerCoffee = 0; // ...} // ...}
Tämä koodi antaa Kahvikuppi
vastustaa haluttua käyttäytymistä. Tämän lähestymistavan ongelmana on, että erityisiä arvoja ei ymmärretä helposti, ja ne vaikeuttavat koodin muuttamista. Vaikka kuvailisit erityisiä arvoja kommentissa, muilla ohjelmoijilla voi kestää kauemmin ymmärtää koodisi toimintaa. Lisäksi he eivät ehkä koskaan ymmärrä koodiasi. He voivat käyttää luokkaa väärin tai muuttaa sitä siten, että he tuovat virheen.
Esimerkiksi, jos myöhemmin joku lisää 20 unssin kupin virtuaalikahvilan tarjontaan, olisi mahdollista pitää kupin mukana jopa 592 millilitraa (ml) kahvia. Jos ohjelmoija lisää uuden kuppikoon tajuamatta, että käytät 500 ml osoittaaksesi, että kuppi on pestävä, on todennäköistä, että vika otetaan käyttöön. Jos virtuaalisen kahvilasi asiakas osti 20 unssin kupin ja otti sitten ison 92 ml: n annoksen, hänellä olisi sitten kuppiin jäljellä tarkalleen 500 ml. Asiakas olisi järkyttynyt ja tyytymätön, kun vain 92 ml juomisen jälkeen kuppi katosi kädestään ja ilmestyi pesualtaaseen. Ja vaikka muutoksen tekevä ohjelmoija huomaisi, että käytit erityistä arvoa, pesemättömälle attribuutille olisi valittava toinen erityinen arvo.
Parempi lähestymistapa tähän tilanteeseen on saada erillinen kenttä erillisen määritteen mallintamiseksi:
// Lähdepaketissa tiedostokentissä / ex2 / CoffeeCup.java-luokka CoffeeCup {private int innerCoffee; yksityiset loogiset tarpeetPesu; public boolean isReadyForNextUse () {// Jos kahvikuppi ei ole pesty, se // ei ole valmis seuraavaa käyttöä varten return! needsWashing; } public void setCustomerDone () {needsWashing = true; // ...} public void wash () {needsWashing = false; // ...} // ...}
Tässä sisäinen kahvi
kenttää käytetään vain mallinnamaan kahvimäärää kuppi-määritteessä. Kuppi tarvitsee pestä -ominaisuuden mallintaa needsPesu
ala. Tämä kaavio on helpommin ymmärrettävissä kuin edellinen järjestelmä, jossa käytettiin erityistä arvoa sisäinen kahvi
eikä estäisi jotakuta laajentamasta enimmäisarvoa sisäinen kahvi
.
Vakioiden käyttö
Toinen nyrkkisääntö, jota on noudatettava kenttien luomisessa, on käyttää vakioita (staattisia lopullisia muuttujia) vakioarvoille, jotka välitetään, palautetaan tai joita käytetään menetelmissä. Jos menetelmä odottaa yhtä lopullisesta vakioarvojen joukosta yhdessä sen parametreista, vakioiden määritteleminen auttaa tekemään asiakasohjelmoijille selvemmäksi, mitä kyseisessä parametrissa on välitettävä. Samoin, jos menetelmä palauttaa yhden rajallisesta arvojoukosta, vakioiden ilmoittaminen tekee asiakasohjelmoijille selvemmäksi, mitä odottaa lähtöä. Esimerkiksi tämä on helpompi ymmärtää:
jos (cup.getSize () == Kahvikuppi TALL) {}
kuin on ymmärtää tämä:
jos (cup.getSize () == 1) {}
Sinun tulisi myös määritellä vakiot sisäiseen käyttöön luokan menetelmillä - vaikka niitä ei käytettäisikään luokan ulkopuolella -, jotta ne olisi helpompi ymmärtää ja muuttaa. Vakioiden käyttö tekee koodista joustavampaa. Jos huomaat, että olet laskenut arvon väärin etkä käyttänyt vakiota, joudut käymään läpi koodisi ja muuttamaan jokaista kovakoodatun arvon esiintymää. Jos kuitenkin käytit vakiota, sinun on muutettava sitä vain siellä, missä se on määritelty vakiona.
Vakiot ja Java-kääntäjä
Hyödyllinen tieto Java-kääntäjästä on, että se kohtelee staattisia lopullisia kenttiä (vakioita) eri tavalla kuin muunlaisia kenttiä. Viitteet staattisiin lopullisiin muuttujiin, jotka on alustettu kääntöaikavakioon, ratkaistaan käännösaikana vakioarvon paikalliseen kopioon. Tämä pätee kaikkien primitiivityyppisten ja -tyyppisten vakioiden osalta java.lang.String
.
Normaalisti, kun luokkasi viittaa toiseen luokkaan - sanokaa, luokka java.lang.Math
- Java-kääntäjä sijoittaa symbolisia viittauksia luokkaan Matematiikka
luokan luokkatiedostoon. Esimerkiksi, jos luokkasi menetelmä käyttää Math.sin ()
, luokkatiedostosi sisältää kaksi symbolista viittausta Matematiikka
:
- Yksi symbolinen viittaus luokkaan
Matematiikka
- Yksi symbolinen viittaus
Matematiikka
onsynti()
menetelmä
Suorita luokkasi koodi, johon viitataan Math.sin ()
, JVM: n on kuormitettava luokka Matematiikka
symbolisten viitteiden ratkaisemiseksi.
Jos toisaalta koodisi viittasi vain staattiseen lopulliseen luokan muuttujaan PI
ilmoitettu luokassa Matematiikka
, Java-kääntäjä ei aseta mitään symbolista viittausta Matematiikka
luokan luokkatiedostossa. Sen sijaan se yksinkertaisesti sijoittaa kopion kirjaimellisesta arvosta Math.PI
luokan luokkatiedostoon. Suorita luokan käyttämä koodi, joka käyttää Math.PI
vakiona, JVM: n ei tarvitse ladata luokkaa Matematiikka
.
Java-kääntäjän tämän ominaisuuden tulos on se, että JVM: n ei tarvitse työskennellä kovemmin vakioiden käyttämiseksi kuin literaalien käyttämisessä. Vakioiden suosiminen literaaleihin on yksi harvoista suunnitteluohjeista, joka parantaa ohjelman joustavuutta vaarantamatta ohjelman suorituskyvyn heikkenemistä.
Kolme erilaista menetelmää
Tämän artikkelin loppuosa käsittelee menetelmien suunnittelutekniikoita, jotka koskevat menetelmän käyttämiä tai muokkaamia tietoja. Tässä yhteydessä haluaisin tunnistaa ja nimetä Java-ohjelmien kolme perustyyppiä: hyödyllisyysmenetelmä tilanäkymämenetelmä, ja tilanvaihtomenetelmä.
Hyödyllisyysmenetelmä
Apuohjelma-menetelmä on luokkamenetelmä, joka ei käytä tai muokkaa luokansa tilaa (luokan muuttujia). Tällainen menetelmä tarjoaa yksinkertaisesti hyödyllisen palvelun, joka liittyy sen objektiluokkaan.
Joitakin esimerkkejä Java-sovellusliittymän hyödyllisyysmenetelmistä ovat:
- (Luokassa
Kokonaisluku
)julkinen staattinen int toString (int i)
- palauttaa uudenMerkkijono
objekti, joka edustaa määritettyä kokonaislukua radiksissa 10 - (Luokassa
Matematiikka
)julkinen staattinen natiivi kaksinkertainen cos (kaksinkertainen a)
- palauttaa kulman trigonometrisen kosinin
Tilanäkymämenetelmä
Tilanäkymämenetelmä on luokka- tai instanssimenetelmä, joka palauttaa osan näkymästä luokan tai objektin sisäisestä tilasta muuttamatta kyseistä tilaa. (Tällainen menetelmä jättää räikeästi huomiotta Heisenbergin epävarmuusperiaatteen - katso Resurssit, jos tarvitset päivitystä tälle periaatteelle.) Tilanäkymämenetelmä voi yksinkertaisesti palauttaa luokan tai esiintymämuuttujan arvon tai palauttaa arvon, joka on laskettu useita luokka- tai instanssimuuttujia.
Joitakin esimerkkejä Java-sovellusliittymän tilanäkymämenetelmistä ovat:
- (Luokassa
Esine
)public String toString ()
- palauttaa objektin merkkijonon - (Luokassa
Kokonaisluku
)julkisen tavun tavuarvo ()
- palauttaa arvonKokonaisluku
esine tavuna - (Luokassa
Merkkijono
)public int indexOf (int ch)
- palauttaa indeksin määritetyn merkin ensimmäisen esiintymisen merkkijonossa
Tilanvaihtomenetelmä
Tilamuutosmenetelmä on menetelmä, joka voi muuttaa sen luokan tilan, jossa menetelmä on ilmoitettu, tai jos esiintymämenetelmänä, objektin, johon sitä kutsutaan. Kun tilanvaihtomenetelmää käytetään, se edustaa "tapahtumaa" luokalle tai objektille. Menetelmän koodi "käsittelee" tapahtumaa, mikä voi muuttaa luokan tai objektin tilaa.
Joitakin esimerkkejä Java-sovellusliittymän tilanvaihtomenetelmistä ovat:
- (Luokassa
StringBuffer
)julkinen StringBuffer-liite (int i)
- liittää merkkijonon merkkijononint
argumenttiStringBuffer
- (Luokassa
Hashtable
)julkinen synkronoitu void clear ()
- tyhjentääHashtable
niin että se ei sisällä avaimia - (Luokassa
Vektori
)julkinen lopullinen synkronoitu void addElement (Object obj)
- lisää määritetyn komponentinVektori
, lisäämällä sen kokoa yhdellä
Menetelmäkytkennän minimointi
Näiden apuohjelmien, tilanäkymän ja tilanmuutosmenetelmien määritelmien avulla olet valmis keskustelemaan menetelmäkytkennästä.
Suunnitellessasi menetelmiä yhden tavoitteistasi tulisi olla minimointi kytkentä - menetelmän ja sen ympäristön (muut menetelmät, objektit ja luokat) keskinäisen riippuvuuden aste. Mitä vähemmän kytkentää menetelmän ja sen ympäristön välillä on, sitä riippumattomampi menetelmä on ja sitä joustavampi rakenne on.
Menetelmät tiedonmuuntajina
Yhdistämisen ymmärtäminen auttaa ajattelemaan menetelmiä puhtaasti datan muuntajina. Menetelmät hyväksyvät tiedot syötteeksi, suorittavat toimenpiteitä kyseisille tiedoille ja tuottavat tietoja tuotoksena. Menetelmän kytkentäaste määräytyy ensisijaisesti sen mukaan, mihin se saa syöttötiedonsa ja mihin laittaa lähtötiedonsa.
Kuvassa 1 on graafinen kuvaus menetelmästä tiedonsiirtona: Datavuokaavio strukturoidusta (ei olio-orientoidusta) suunnittelusta.
Tulo ja lähtö
Javan menetelmä voi saada tulotietoja monista lähteistä:
- Se voi vaatia, että soittaja määrittää syöttötiedonsa parametreiksi, kun sitä kutsutaan
- Se voi napata tietoja mistä tahansa käytettävissä olevasta luokan muuttujasta, kuten luokan omasta luokan muuttujasta tai mistä tahansa toisen luokan käytettävissä olevasta luokan muuttujasta
- Jos kyseessä on ilmentymämenetelmä, se voi napata ilmentymämuuttujia objektista, johon se on kutsuttu
Samoin menetelmä voi ilmaista tuotoksensa monissa paikoissa:
- Se voi palauttaa arvon, joko primitiivisen tyypin tai objektiviitteen
- Se voi muuttaa objekteja, joihin viitataan parametreina
- Se voi muuttaa minkä tahansa oman luokansa luokan muuttujia tai minkä tahansa toisen luokan käytettävissä olevia luokan muuttujia
- Jos kyseessä on instanssimenetelmä, se voi muuttaa minkä tahansa objektin ilmentymämuuttujia, joihin sitä käytettiin
- Se voi aiheuttaa poikkeuksen
Huomaa, että parametrit, palautusarvot ja heitetyt poikkeukset eivät ole ainoat menetelmien tulot ja lähdöt, jotka on mainittu yllä olevissa luetteloissa. Ilmentymän ja luokan muuttujia käsitellään myös syötteenä ja tuotoksena. Tämä saattaa tuntua ei-intuitiiviselta olio-suuntautuneesta näkökulmasta, koska Java-ilmentymien ja luokkien muuttujien käyttö on "automaattista" (sinun ei tarvitse siirtää mitään nimenomaisesti menetelmälle). Kun yrität mitata menetelmän kytkentää, sinun on kuitenkin tarkasteltava käytetyn ja koodin muokkaaman datan tyyppiä ja määrää riippumatta siitä, oliko koodin pääsy kyseisiin tietoihin "automaattinen" vai ei.
Vähintään kytketyt hyödyllisyysmenetelmät
Pienin yhdistetty menetelmä, joka on mahdollista Java-tilassa, on apuohjelma, joka:
- Ottaa syötteen vain sen parametreista
- Ilmaisee tuotoksensa vain parametrien tai palautusarvon kautta (tai heittämällä poikkeuksen)
- Hyväksyy syötteenä vain tiedot, joita menetelmä todella tarvitsee
- Palauttaa vain tuotoksena menetelmän tosiasiallisesti tuottamat tiedot
Hyvä apuohjelma
Esimerkiksi menetelmä convertOzToMl ()
alla esitetty hyväksyy int
sen ainoana syötteenä ja palauttaa int
ainoana tuotoksena: