Ohjelmointi

Kapselointi ei ole tietojen piilottaminen

Sanat ovat liukkaita. Kuten Lewis Carrollin julistama Humpty Dumpty Katselulasin läpi, "Kun käytän sanaa, se tarkoittaa vain sitä, mitä valitsen sen tarkoittavan - ei enempää eikä vähemmän." Varmasti sanojen yleinen käyttö kapselointi ja tietojen piilottaminen näyttää noudattavan tätä logiikkaa. Kirjoittajat tekevät harvoin eroa näiden kahden välillä ja väittävät usein suoraan, että he ovat samat.

Tekeekö se niin? Ei minulle. Jos se olisi yksinkertaisesti sanojen kysymys, en kirjoittaisi sanaakaan asiasta. Mutta näiden termien takana on kaksi erillistä käsitettä, käsitteet syntyvät erikseen ja ymmärretään parhaiten erikseen.

Kapselointi tarkoittaa tietojen niputtamista menetelmiin, jotka toimivat kyseisillä tiedoilla. Usein tätä määritelmää tulkitaan väärin tarkoittamalla, että data on jotenkin piilotettu. Java-ohjelmassa sinulla voi olla kapseloituja tietoja, joita ei ole piilotettu lainkaan.

Tietojen piilottaminen ei kuitenkaan ole koko tiedon piilottamisen laajuutta. David Parnas esitteli ensimmäisen kerran piiloutuneen tiedon käsitteen noin vuonna 1972. Hän väitti, että järjestelmän modulaarisuuden ensisijaisten kriteerien tulisi koskea kriittisten suunnittelupäätösten piilottamista. Hän painotti piilottavansa "vaikeat suunnittelupäätökset tai todennäköisesti muuttuvat suunnittelupäätökset". Tietojen piilottaminen tällä tavalla eristää asiakkaita vaatimasta moduulin käytön suunnittelun läheistä tuntemusta ja näiden päätösten muuttamisen vaikutuksista.

Tässä artikkelissa tutkin kapseloinnin ja piiloutuneen tiedon eroa kehittämällä esimerkkikoodia. Keskustelu osoittaa, kuinka Java helpottaa kapselointia ja tutkii kapseloinnin kielteisiä seurauksia piilottamatta tietoja. Esimerkit osoittavat myös, miten luokan suunnittelua voidaan parantaa tiedon piilottamisen periaatteen avulla.

Paikkaluokka

Kun tietoisuus langattoman Internetin laajasta potentiaalista kasvaa, monet asiantuntijat odottavat sijaintiin perustuvien palvelujen tarjoavan mahdollisuuden ensimmäiselle langattomalle tappajasovellukselle. Tämän artikkelin esimerkkikoodiksi olen valinnut luokan, joka edustaa pinnan maantieteellistä sijaintia maan pinnalla. Verkkotunnuskokonaisuutena nimetty luokka Sijainti, edustaa GPS-tietoja (Global Position System). Ensimmäinen leikkaus luokassa näyttää yhtä yksinkertaiselta kuin:

julkisen luokan sijainti {julkinen kaksinkertainen leveysaste; julkinen kaksinkertainen pituusaste; } 

Luokka sisältää kaksi tietoa: GPS leveysaste ja pituusaste. Nykyisessä, Sijainti ei ole muuta kuin pieni tietopussi. Kuitenkin, Sijainti on luokka, ja Sijainti objektit voidaan instantisoida luokan avulla. Voit hyödyntää näitä objekteja, luokka PositionUtility sisältää menetelmät määritettyjen etäisyyden ja suunnan - eli suunnan - laskemiseksi Sijainti esineet:

public class PositionUtility {julkinen staattinen kaksinkertainen etäisyys (Position position1, Position position2) {// Laske ja palauta määritettyjen sijaintien välinen etäisyys. } julkinen staattinen kaksoissuunta (Sijainnin sijainti1, Paikan sijainti2) {// Laske ja palauta otsikko paikasta1 asentoon2. }} 

Jättäen varsinaisen toteutuskoodin etäisyys- ja suuntalaskelmiin.

Seuraava koodi edustaa tyypillistä Sijainti ja PositionUtility:

// Luo koti, joka edustaa taloni sijaintia myHouse = new Position (); myHouse.latitude = 36.538611; oma talo.pituus = -121,797500; // Luo paikallinen kahvila edustava sijainti Position coffeeShop = new Position (); coffeeShop.latitude = 36.539722; coffeeShop.longitude = -121,907222; // Käytä PositionUtilityä laskemaan etäisyys ja suunta talostani // paikalliseen kahvilaan. kaksinkertainen etäisyys = PositionUtility.distance (myHouse, coffeeShop); kaksinkertainen otsikko = PositionUtility.heading (myHouse, coffeeShop); // Tulosta tulokset System.out.println ("Talostani osoitteessa (" + myHouse.latitude + "," + myHouse.longitude + ") kahvilaan (" + coffeeShop.latitude + "," + coffeeShop. pituusaste + ") on etäisyys" + etäisyys + "otsikossa" + suunta + "astetta."); 

Koodi tuottaa alla olevan lähdön, mikä osoittaa, että kahvila on suoraan taloni länteen (270,8 astetta) 6,09: n etäisyydellä. Myöhemmässä keskustelussa käsitellään etäisyysyksikköjen puutetta.

 ==================================================== ================= Talostani osoitteessa (36.538611, -121.7975) kahvilaan (36.539722, -121.907222) on 6.0873776351893385 etäisyys 270.7547022304523 astetta. ==================================================== ================= 

Sijainti, PositionUtility, ja niiden koodin käyttö on hieman huolestuttavaa eikä todellakaan kovin olio-suuntautunutta. Mutta miten se voi olla? Java on olio-kieli, ja koodi käyttää objekteja!

Vaikka koodi saattaa käyttää Java-objekteja, se tekee sen tavalla, joka muistuttaa menneisyyden aikakautta: tietorakenteilla toimivat apuohjelmatoiminnot. Tervetuloa vuoteen 1972! Kun presidentti Nixon käpertyi salaisista nauhoituksista, Fortranin menettelykielellä koodaavat atk-ammattilaiset käyttivät innokkaasti uutta kansainvälistä matematiikan ja tilastokirjastoa (IMSL) juuri tällä tavalla. Koodivarastot, kuten IMSL, olivat täynnä funktioita numeerisiin laskelmiin. Käyttäjät välittivät tietoja näille toiminnoille pitkissä parametriluetteloissa, jotka toisinaan sisälsivät paitsi syötteen myös ulostulodatarakenteet. (IMSL on kehittynyt jatkuvasti vuosien varrella, ja versio on nyt saatavana Java-kehittäjille.)

Nykyisessä suunnittelussa Sijainti on yksinkertainen tietorakenne ja PositionUtility on kirjastotoimintojen IMSL-tyyppinen arkisto, joka toimii Sijainti tiedot. Kuten yllä oleva esimerkki osoittaa, nykyaikaiset olio-kielet eivät välttämättä sulje pois vanhentuneiden menettelytapojen käyttämistä.

Tietojen ja menetelmien niputtaminen

Koodia voidaan helposti parantaa. Ensinnäkin, miksi sijoittaa tiedot ja toiminnot, jotka toimivat kyseisillä tiedoilla erillisissä moduuleissa? Java-luokat sallivat tietojen ja menetelmien niputtamisen yhdessä:

public class Position {public double distance (Position position) {// Laske ja palauta etäisyys objektista määritettyyn // sijaintiin. } public double title (Position position) {// Laske ja palauta otsikko tältä objektilta määritettyyn // sijaintiin. } julkinen kaksinkertainen leveysaste; julkinen kaksinkertainen pituusaste; } 

Paikkatietokohteiden ja toteutuskoodin asettaminen etäisyyden ja suunnan laskemiseksi samaan luokkaan poistaa erillisen tarpeen PositionUtility luokassa. Nyt Sijainti alkaa muistuttaa todellista olio-luokkaa. Seuraava koodi käyttää tätä uutta versiota, joka niputtaa tiedot ja menetelmät yhteen:

Position myHouse = uusi sijainti (); myHouse.latitude = 36.538611; oma talo.pituus = -121,797500; Position coffeeShop = uusi sijainti (); coffeeShop.latitude = 36.539722; coffeeShop.longitude = -121,907222; kaksinkertainen etäisyys = myHouse.distance (kahvila); kaksinkertainen otsikko = myHouse.heading (kahvila); System.out.println ("Talostani osoitteessa (" + myHouse.latitude + "," + myHouse.longitude + ") kahvilaan (" + coffeeShop.latitude + "," + coffeeShop.longitude + ") on etäisyys "+ etäisyys +" otsikossa "+ otsikko +" astetta. "); 

Lähtö on identtinen kuin aiemmin, ja mikä tärkeintä, yllä oleva koodi näyttää luonnollisemmalta. Edellinen versio läpäisi kaksi Sijainti vastustaa erillisen apuohjelmaluokan funktiota etäisyyden ja suunnan laskemiseksi. Koodissa lasketaan otsikko menetelmän kutsulla util.heading (myHouse, kahvila) ei ilmoittanut selvästi laskennan suuntaa. Kehittäjän on muistettava, että apuohjelmatoiminto laskee otsikon ensimmäisestä parametrista toiseen.

Vertailun vuoksi yllä oleva koodi käyttää lausetta myHouse.heading (kahvila) laskea sama otsikko. Puhelun semantiikka osoittaa selvästi, että suunta kulkee talostani kahvilaan. Kahden argumentin funktion muuntaminen otsikko (sijainti, sijainti) yhden argumentin funktioon position.heading (Sijainti) tunnetaan curry toiminto. Currying erikoistuu funktioon tehokkaasti ensimmäiseen argumenttiinsa, mikä johtaa selkeämpään semantiikkaan.

Menetelmien sijoittaminen hyödyntämällä Sijainti luokan tiedot Sijainti luokka itse tekee curry-toiminnot etäisyys ja otsikko mahdollista. Toimintojen kutsurakenteen muuttaminen tällä tavalla on merkittävä etu menettelykieliin verrattuna. Luokka Sijainti edustaa nyt abstraktia tietotyyppiä, joka kapseloi datan ja kyseisiä tietoja käyttävät algoritmit. Käyttäjän määrittelemänä tyypinä Sijainti Esineet ovat myös ensiluokkaisia ​​kansalaisia, jotka nauttivat kaikista Java-kielityyppijärjestelmän eduista.

Kielitoiminto, joka niputtaa tiedot ja kyseisille tiedoille suoritettavat toiminnot, on kapselointi. Huomaa, että kapselointi ei takaa tietosuojaa eikä tietojen piilottamista. Kapselointi ei myöskään takaa yhtenäistä luokan suunnittelua. Näiden laatumääritteiden saavuttaminen vaatii tekniikoita, jotka ovat kielen tarjoaman kapseloinnin ulkopuolella. Kuten tällä hetkellä toteutettu, luokka Sijainti ei sisällä tarpeetonta tai ei-toisiinsa liittyvää tietoa ja menetelmiä, mutta Sijainti paljastaa molemmat leveysaste ja pituusaste raakana. Tämä sallii minkä tahansa luokan asiakkaan Sijainti muuttaa suoraan sisäistä tietokohtaa ilman käyttäjän väliintuloa Sijainti. Kapselointi ei selvästikään riitä.

Puolustava ohjelmointi

Sisäisten tietokohteiden paljastamisen seurausten tutkimiseksi oletetaan, että päätän lisätä hieman puolustavaa ohjelmointia Sijainti rajoittamalla leveys- ja pituusaste GPS: n määrittelemille alueille. Leveysaste on alueella [-90, 90] ja pituusaste (-180, 180). leveysaste ja pituusaste sisään SijaintiNykyinen toteutus tekee tämän puolustavan ohjelmoinnin mahdottomaksi.

Määritteiden tekeminen leveys- ja pituusasteista yksityinen luokan jäsenet Sijainti ja lisäämällä yksinkertaisia ​​yhteys- ja mutaattorimenetelmiä, joita kutsutaan yleisesti gettereiksi ja settereiksi, saadaan yksinkertainen ratkaisu raakatietokohteiden paljastamiseen. Seuraavassa esimerkkikoodissa setterimenetelmät seulovat asianmukaisesti arvon leveysaste ja pituusaste. Sen sijaan, että heittäisin poikkeuksen, määritän suorittavan modulo-aritmeettisen arvon tuloarvoille pitääkseen sisäiset arvot määritetyillä alueilla. Esimerkiksi yritettäessä asettaa leveysaste 181,0: ksi sisäinen asetus on -179,0 leveysaste.

Seuraava koodi lisää getter- ja setter-menetelmät yksityisten tietojenkäsittelijöiden käyttämiseksi leveysaste ja pituusaste:

julkisen luokan sijainti {julkinen sijainti (kaksinkertainen leveysaste, kaksinkertainen pituusaste) {setLatitude (leveysaste); setLongitude (pituusaste); } public void setLatitude (double latitude) {// Varmista -90 <= leveys <= 90 modulo-aritmeettisesti. // Koodia ei näytetä. // Aseta sitten esiintymämuuttuja. this.latitude = leveysaste; } public void setLongitude (double longitude) {// Varmista, että -180 <pituusaste = = 180 modulo-aritmeettisesti. // Koodia ei näytetä. // Aseta sitten esiintymämuuttuja. tämä.pituus = pituusaste; } public double getLatitude () {return latitude; } public double getLongitude () {paluu pituusaste; } public double distance (Position position) {// Laske ja palauta etäisyys objektista määritettyyn // sijaintiin. // Koodia ei näytetä. } public double title (Position position) {// Laske ja palauta otsikko tältä objektilta määritettyyn // sijaintiin. } yksityinen kaksinkertainen leveysaste; yksityinen kaksinkertainen pituusaste; } 

Käyttämällä yllä olevaa versiota Sijainti vaatii vain pieniä muutoksia. Ensimmäisenä muutoksena, koska yllä oleva koodi määrittää rakentajan, joka vie kaksi kaksinkertainen argumentteja, oletusrakentaja ei ole enää käytettävissä. Seuraava esimerkki käyttää uutta konstruktoria sekä uusia getter-menetelmiä. Tuotos pysyy samana kuin ensimmäisessä esimerkissä.

Position myHouse = uusi sijainti (36.538611, -121.797500); Position coffeeShop = uusi sijainti (36.539722, -121.907222); kaksinkertainen etäisyys = myHouse.distance (kahvila); kaksinkertainen otsikko = myHouse.heading (kahvila); System.out.println ("Talostani osoitteessa (" + myHouse.getLatitude () + "," + myHouse.getLongitude () + ") kahvilaan osoitteessa (" + coffeeShop.getLatitude () + "," + coffeeShop.getLongitude () + ") on etäisyys" + etäisyys + "otsikossa" + otsikko + "astetta."); 

Valitsemalla rajoittaa hyväksyttäviä arvoja leveysaste ja pituusaste setterimenetelmillä on tiukasti suunnittelupäätös. Kapseloinnilla ei ole merkitystä. Toisin sanoen kapselointi, joka ilmenee Java-kielellä, ei takaa sisäisten tietojen suojausta. Kehittäjänä voit vapaasti paljastaa luokan sisäosat. Sinun tulisi kuitenkin rajoittaa sisäisten tietokohteiden käyttöä ja muokkaamista käyttämällä getter- ja setter-menetelmiä.

Potentiaalisen muutoksen eristäminen

Sisäisten tietojen suojaaminen on vain yksi monista huolenaiheista, jotka ohjaavat suunnittelupäätöksiä kielen kapseloinnin päälle. Eristäminen muutokseen on toinen asia. Luokan sisäisen rakenteen muuttaminen ei saisi, mikäli mahdollista, vaikuttaa asiakasluokkiin.

Esimerkiksi totesin aiemmin, että etäisyyden laskenta luokassa Sijainti ei ilmoittanut yksiköitä. Ilmoitettu etäisyys 6,09 talostani kahvilaan tarvitsee selvästi mittayksikön, jotta se olisi hyödyllinen. Tiedän ehkä suunnan, mutta en tiedä, kävelenkö 6,09 metriä, ajaanko 6,09 mailia vai lentäisinkö 6,09 tuhatta kilometriä.

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