Programiranje

Kapsulacija ni skrivanje informacij

Besede so spolzke. Kot Humpty Dumpty, razglašen v Lewisu Carrollu Skozi ogledalo, "Ko uporabim besedo, pomeni ravno tisto, kar sem izbral - niti več niti manj." Vsekakor pogosta raba besed inkapsulacija in skrivanje informacij zdi se, da sledi tej logiki. Avtorji med njima redko ločijo in pogosto neposredno trdijo, da sta enaka.

Ali je to res tako? Ni zame. Če bi šlo zgolj za besede, o tej zadevi ne bi napisal niti druge besede. Toda za temi izrazoma se skrivata dva različna koncepta, ki nastaneta ločeno in jih je najbolje razumeti ločeno.

Kapsulacija se nanaša na združevanje podatkov z metodami, ki delujejo na te podatke. Pogosto je ta definicija napačno razlagana, da pomeni, da so podatki nekako skriti. V Javi imate lahko zaprte podatke, ki sploh niso skriti.

Vendar skrivanje podatkov ni celoten obseg skrivanja informacij. David Parnas je prvič predstavil koncept skrivanja informacij okoli leta 1972. Trdil je, da bi se morala glavna merila za modularizacijo sistema nanašati na skrivanje kritičnih oblikovalskih odločitev. Poudaril je, da skriva "težke oblikovalske odločitve ali oblikovalske odločitve, ki se bodo verjetno spremenile." Skrivanje informacij na ta način strank loči od potrebe po natančnem poznavanju zasnove za uporabo modula in od učinkov spreminjanja teh odločitev.

V tem članku raziskujem razliko med enkapsulacijo in skrivanjem informacij z razvojem vzorčne kode. Razprava prikazuje, kako Java olajša inkapsulacijo in raziskuje negativne posledice enkapsulacije brez skrivanja podatkov. Primeri kažejo tudi, kako izboljšati oblikovanje razredov po principu skrivanja informacij.

Položaj razreda

Z naraščajočim zavedanjem o velikem potencialu brezžičnega interneta mnogi strokovnjaki pričakujejo, da bodo storitve, ki temeljijo na lokaciji, priložnost za prvo aplikacijo brezžičnega morilca. Za vzorčno kodo tega članka sem izbral razred, ki predstavlja geografsko lego točke na zemeljski površini. Kot entiteta domene je razred z imenom Položajpredstavlja informacije o sistemu globalnega položaja (GPS). Prvi rez na predavanju je videti tako preprosto kot:

položaj javnega razreda {javna dvojna širina; javna dvojna dolžina; } 

Predavanje vsebuje dva podatkovna elementa: GPS zemljepisna širina in zemljepisne dolžine. Trenutno, Položaj ni nič drugega kot majhna vrečka podatkov. Kljub temu Položaj je razred in Položaj predmeti se lahko ustvarijo z uporabo razreda. Če želite uporabiti te predmete, class PositionUtility vsebuje metode za izračun razdalje in smeri - to je smeri - med navedenima Položaj predmeti:

javni razred PositionUtility {javna statična dvojna razdalja (Position position1, Position position2) {// Izračunajte in vrnite razdaljo med navedenimi položaji. } javni statični dvojni naslov (Položaj položaj1, Položaj položaj2) {// Izračunaj in vrni naslov s položaja1 na položaj2. }} 

Za izračune razdalje in smeri izpuščam dejansko izvedbeno kodo.

Naslednja koda predstavlja tipično uporabo Položaj in PositionUtility:

// Ustvari položaj, ki predstavlja mojo hišo Position myHouse = nov položaj (); myHouse.latitude = 36,538611; myHouse.longitude = -121,797500; // Ustvari položaj, ki predstavlja lokalno kavarno Position coffeeShop = nov položaj (); coffeeShop.latitude = 36,539722; coffeeShop.longitude = -121.907222; // Uporabite pripomoček PositionUtility za izračun razdalje in smeri od moje hiše // do lokalne kavarne. dvojna razdalja = PositionUtility.distance (myHouse, coffeeShop); dvojni naslov = PositionUtility.heading (myHouse, coffeeShop); // Natisni rezultate System.out.println ("Od moje hiše na (" + myHouse.latitude + "," + myHouse.latitude + ") do kavarne na (" + coffeeShop.latitude + "," + coffeeShop. zemljepisna dolžina + ") je razdalja" + razdalja + "pri naslovu" + naslov + "stopinj."); 

Koda ustvari spodnji izhod, kar pomeni, da je kavarna proti zahodu (270,8 stopinje) od moje hiše na razdalji 6,09. Kasnejša razprava obravnava pomanjkanje enot razdalj.

 ==================================================== ================= Od moje hiše na (36.538611, -121.7975) do kavarne na (36.539722, -121.907222) je razdalja 6.0873776351893385 v smeri 270.7547022304523 stopinj. ==================================================== ================= 

Položaj, PositionUtility, njihova uporaba kode pa je nekoliko zaskrbljujoča in zagotovo ni preveč objektno usmerjena. Kako pa je to mogoče? Java je objektno usmerjen jezik in koda uporablja predmete!

Čeprav koda lahko uporablja predmete Java, to počne na način, ki spominja na preteklo dobo: funkcije pripomočkov, ki delujejo na podatkovnih strukturah. Dobrodošli v letu 1972! Medtem ko se je predsednik Nixon drvel ob tajnih snemanjih kaset, so računalniški strokovnjaki, ki kodirajo v postopkovnem jeziku Fortran, navdušeno uporabljali novo Mednarodno knjižnico matematike in statistike (IMSL) prav na ta način. Repozitoriji kod, kot je IMSL, so bili polni funkcij za numerične izračune. Uporabniki so tem funkcijam posredovali podatke v dolgih seznamih parametrov, ki so včasih vključevali ne samo vhodne, temveč tudi izhodne podatkovne strukture. (IMSL se je v preteklih letih še naprej razvijal in različica je zdaj na voljo razvijalcem Jave.)

V sedanji zasnovi je Položaj je preprosta struktura podatkov in PositionUtility je shramba knjižničnih funkcij v slogu IMSL, ki deluje Položaj podatkov. Kot kaže zgornji primer, sodobni objektno usmerjeni jeziki ne izključujejo nujno uporabe zastarelih postopkovnih tehnik.

Združevanje podatkov in metod

Kode je mogoče enostavno izboljšati. Za začetek, zakaj bi podatke in funkcije, ki delujejo na teh podatkih, postavili v ločene module? Razredi Java omogočajo združevanje podatkov in metod:

javni razred Položaj {javna dvojna razdalja (Položaj položaja) {// Izračunaj in vrni razdaljo od tega predmeta na določen // položaj. } javni dvojni naslov (Položaj položaja) {// Izračunaj in vrni naslov tega predmeta na določen // položaj. } javna dvojna širina; javna dvojna dolžina; } 

Postavitev postavk podatkov o položaju in izvedbene kode za izračun razdalje in smeri v isti razred odpravi potrebo po ločenem PositionUtility razred. Zdaj Položaj začne spominjati na pravi objektno usmerjen razred. Naslednja koda uporablja to novo različico, ki združuje podatke in metode:

Položaj myHouse = nov položaj (); myHouse.latitude = 36,538611; myHouse.longitude = -121,797500; Position coffeeShop = new Position (); coffeeShop.latitude = 36,539722; coffeeShop.longitude = -121.907222; dvojna razdalja = myHouse.distance (coffeeShop); dvojni naslov = myHouse.heading (coffeeShop); System.out.println ("Od moje hiše na (" + myHouse.latitude + "," + myHouse.longitude + ") do kavarne na (" + coffeeShop.latitude + "," + coffeeShop. Longitude + ") je razdalja "+ razdalja +" pri naslovu "+ naslov +" stopinj. "); 

Izhod je enak kot prej, in kar je še pomembneje, zgornja koda se zdi bolj naravna. Prejšnja različica je prešla dve Položaj predmetom funkcije v ločenem razredu uporabnosti za izračun razdalje in smeri. V tej kodi izračun naslova s ​​klicem metode util.heading (myHouse, coffeeShop) ni jasno navedel smeri izračuna. Razvijalec se mora zavedati, da funkcija pripomočka izračuna naslov od prvega parametra do drugega.

Za primerjavo, zgornja koda uporablja stavek myHouse.heading (coffeeShop) za izračun istega naslova. Semantika klica jasno kaže, da smer vodi od moje hiše do kavarne. Pretvorba funkcije z dvema argumentoma naslov (Položaj, Položaj) na funkcijo z enim argumentom position.heading (Položaj) je znano kot currying funkcijo. Currying je na prvi argument učinkovito specializiral funkcijo, kar ima za posledico jasnejšo semantiko.

Postavitev metod, ki uporabljajo Položaj podatki razreda v Položaj class sam naredi currying funkcij razdalja in naslov mogoče. Sprememba strukture klicev funkcij na ta način je pomembna prednost pred postopkovnimi jeziki. Razred Položaj zdaj predstavlja abstraktni tip podatkov, ki zajema podatke in algoritme, ki delujejo na te podatke. Kot uporabniško določena vrsta Položaj predmeti so tudi prvovrstni državljani, ki uživajo vse prednosti sistema jezikovnega tipa Java.

Jezikovna naprava, ki združuje podatke z operacijami, ki se izvajajo na teh podatkih, je enkapsulacija. Upoštevajte, da enkapsulacija ne zagotavlja niti zaščite podatkov niti skrivanja informacij. Kapsulacija tudi ne zagotavlja kohezivnega oblikovanja razreda. Da bi dosegli te kakovostne lastnosti oblikovanja, so potrebne tehnike, ki presegajo enkapsulacijo, ki jo zagotavlja jezik. Kot je trenutno izvedeno, razred Položaj ne vsebuje odvečnih ali nepovezanih podatkov in metod, vendar Položaj izpostavlja oboje zemljepisna širina in zemljepisne dolžine v surovi obliki. To omogoča vsakemu razredu Položaj za neposredno spremembo katerega koli notranjega podatka brez kakršnega koli posredovanja Položaj. Jasno je, da kapsulacija ni dovolj.

Obrambno programiranje

Recimo, da se za nadaljnje preiskovanje posledic izpostavljenosti notranjih podatkovnih postavk odločim dodati malo obrambnega programiranja Položaj z omejitvijo zemljepisne širine in dolžine na območja, ki jih določa GPS. Zemljepisna širina pade v območju [-90, 90] in zemljepisna dolžina v območju (-180, 180]. Izpostavljenost podatkovnih postavk zemljepisna širina in zemljepisne dolžine v PoložajTrenutna izvedba onemogoča to obrambno programiranje.

Izdelava atributov zemljepisne širine in dolžine zasebno podatkovni člani razreda Položaj in dodajanje preprostih metod dostopa in mutatorjev, imenovanih tudi getterji in setterji, ponuja preprosto sredstvo za razkritje surovih podatkovnih postavk. V spodnjem primeru kode metode nastavitve ustrezno prikažejo notranje vrednosti zemljepisna širina in zemljepisne dolžine. Namesto da bi vrgel izjemo, določim izvajanje modulo aritmetike na vhodnih vrednostih, da ohranim notranje vrednosti znotraj določenih obsegov. Poskus nastavitve zemljepisne širine na primer na primer povzroči notranjo nastavitev -179,0 za zemljepisna širina.

Naslednja koda dodaja metode getter in setter za dostop do članov zasebnih podatkov zemljepisna širina in zemljepisne dolžine:

položaj javnega razreda {javni položaj (dvojna širina, dvojna dolžina) {setLatitude (zemljepisna širina); setLongitude (zemljepisna dolžina); } public void setLatitude (dvojna zemljepisna širina) {// z uporabo modulo aritmetike zagotovite -90 <= latitude <= 90. // Koda ni prikazana. // Nato nastavimo spremenljivko primerka. this.latitude = zemljepisna širina; } public void setLongitude (dvojna dolžina) {// z uporabo modulo aritmetike zagotovimo -180 <longitude <= 180. // Koda ni prikazana. // Nato nastavimo spremenljivko primerka. this. longitude = longitude; } javna dvojna getLatitude () {vrnitev zemljepisne širine; } javna dvojna getLongitude () {vrnitev zemljepisne dolžine; } javna dvojna razdalja (Položaj položaja) {// Izračunaj in vrni razdaljo od tega predmeta na določen // položaj. // Koda ni prikazana. } javni dvojni naslov (Položaj položaja) {// Izračunaj in vrni naslov tega predmeta na določen // položaj. } zasebna dvojna zemljepisna širina; zasebna dvojna dolžina; } 

Z uporabo zgornje različice Položaj zahteva le manjše spremembe. Kot prva sprememba, ker zgornja koda določa konstruktor, ki traja dva dvojno argumentov, privzeti konstruktor ni več na voljo. Naslednji primer uporablja novi konstruktor in nove metode pridobivanja. Izhod ostane enak kot v prvem primeru.

Položaj myHouse = nov položaj (36.538611, -121.797500); Position coffeeShop = new Position (36.539722, -121.907222); dvojna razdalja = myHouse.distance (coffeeShop); dvojni naslov = myHouse.heading (coffeeShop); System.out.println ("Od moje hiše na (" + myHouse.getLatitude () + "," + myHouse.getLongitude () + ") do kavarne na (" + coffeeShop.getLatitude () + "," + coffeeShop.getLongitude () + ") je razdalja" + razdalja + "pri naslovu" + naslov + "stopinj."); 

Izbira omejitve sprejemljivih vrednosti zemljepisna širina in zemljepisne dolžine skozi metode postavljavca je strogo odločitev o oblikovanju. Kapsulacija ne igra vloge. To pomeni, da enkapsulacija, kot se kaže v jeziku Java, ne zagotavlja zaščite notranjih podatkov. Kot razvijalec lahko razkrijete notranjost svojega razreda. Kljub temu bi morali z uporabo getter in setter metod omejiti dostop in spreminjanje notranjih podatkovnih postavk.

Ločevanje potencialnih sprememb

Zaščita notranjih podatkov je le ena izmed številnih skrbi, ki vodi do odločitev o oblikovanju poleg jezikovne kapsulacije. Druga izolacija je sprememba. Spreminjanje notranje strukture razreda ne bi smelo, če je le mogoče, vplivati ​​na razrede odjemalcev.

Na primer, prej sem ugotovil, da je izračun razdalje v razredu Položaj ni navedel enot. Da bi bila koristna, navedena razdalja od moje hiše do kavarne 6,09 očitno potrebuje mersko enoto. Mogoče vem, v katero smer naj grem, ne vem pa, ali naj hodim 6,09 metra, vozim 6,09 milje ali letim 6,09 tisoč kilometrov.

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