Programavimas

Kapsuliavimas nėra informacijos slėpimas

Žodžiai slidūs. Kaip „Humpty Dumpty“, skelbiamas Lewiso Carrollo Pro žvilgsnio stiklą „Kai vartoju žodį, tai reiškia tik tai, ką pasirenku, kad turėčiau galvoje - nei daugiau, nei mažiau“. Be abejo, dažnas žodžių vartojimas kapsuliavimas ir informacijos slėpimas panašu, kad laikosi tos logikos. Autoriai retai skiria abu dalykus ir dažnai tiesiogiai teigia, kad jie yra vienodi.

Ar tai taip daro? Ne man. Jei tai būtų tiesiog žodžių reikalas, aš tuo klausimu neparašyčiau nė vieno žodžio. Tačiau už šių terminų slypi dvi skirtingos sąvokos, sąvokos sukuriamos atskirai ir geriausiai suprantamos atskirai.

Kapsuliavimas reiškia duomenų susiejimą su tais metodais, kurie naudoja tuos duomenis. Dažnai šis apibrėžimas neteisingai suprantamas ir reiškia, kad duomenys yra kažkaip paslėpti. „Java“ sistemoje galite turėti duomenų, kurie visiškai nėra paslėpti.

Tačiau duomenų slėpimas nėra visas informacijos slėpimo mastas. Davidas Parnasas pirmą kartą pristatė informacijos, slepiančios apie 1972 m., Sampratą. Jis teigė, kad pagrindiniai sistemos moduliavimo kriterijai turėtų būti susiję su kritinių projektavimo sprendimų slėpimu. Jis pabrėžė, kad slepia „sudėtingus dizaino sprendimus arba dizaino sprendimus, kurie gali pasikeisti“. Tokiu būdu slėpdami informaciją, klientai izoliuojami nuo reikalavimo, kad modulis būtų naudojamas gerai žinant dizainą, ir nuo tų sprendimų pakeitimo poveikio.

Šiame straipsnyje aš nagrinėju skirtumą tarp susikaupimo ir informacijos slėpimo kuriant kodo pavyzdį. Diskusija parodo, kaip „Java“ palengvina kapsuliavimą ir tiria neigiamus inkapsuliacijos padarinius neslėpdama duomenų. Pavyzdžiai taip pat parodo, kaip patobulinti klasės dizainą pasitelkiant informacijos slėpimo principą.

Pareigos klasė

Vis labiau suprasdami didžiulį belaidžio interneto potencialą, daugelis specialistų tikisi, kad vietomis pagrįstos paslaugos suteiks galimybę pirmajai belaidžio žudiko programai. Šio straipsnio pavyzdiniam kodui pasirinkau klasę, atspindinčią geografinę taško vietą žemės paviršiuje. Kaip domeno subjektas, klasė, pavadinta Pozicija, reiškia visuotinės padėties sistemos (GPS) informaciją. Pirmasis pjūvis klasėje atrodo toks paprastas:

public class Pozicija {public double platitude; viešoji dviguba ilguma; } 

Klasėje yra du duomenų elementai: GPS platuma ir ilguma. Dabar, Pozicija yra ne kas kita, kaip mažas duomenų maišas. Nepaisant to, Pozicija yra klasė ir Pozicija objektai gali būti supaprastinti naudojant klasę. Norėdami panaudoti tuos objektus, klasė „PositionUtility“ yra metodai, kaip apskaičiuoti atstumą ir kryptį, ty kryptį, tarp nurodytų Pozicija objektai:

public class PositionUtility {public static double distance (padėties padėtis1, padėties padėtis2) {// Apskaičiuokite ir grąžinkite atstumą tarp nurodytų pozicijų. } public static double title (Pozicijos padėtis1, Pozicijos padetis2) {// Apskaičiuokite ir grąžinkite antraštę iš pozicijos1 į poziciją2. }} 

Aš praleidžiu faktinį atstumo ir krypties skaičiavimo įgyvendinimo kodą.

Šis kodas reiškia įprastą Pozicija ir „PositionUtility“:

// Sukurkite poziciją, atspindinčią mano namo padėtį myHouse = new Position (); myHouse.latitude = 36.538611; myHouse.longitude = -121,797500; // Sukurkite poziciją, atstovaujančią vietinei kavinei. Position coffeeShop = new Position (); coffeeShop.latitude = 36.539722; coffeeShop.longitude = -121.907222; // Naudokite „PositionUtility“, kad apskaičiuotumėte atstumą ir kryptį nuo mano namų // iki vietos kavinės. dvigubas atstumas = PositionUtility.distance (myHouse, coffeeShop); dviguba antraštė = PositionUtility.heading („myHouse“, „coffeeShop“); // Spausdinti rezultatus System.out.println ("Nuo mano namo adresu (" + myHouse.latitude + "," + myHouse.longitude + ") iki kavinės adresu (" + coffeeShop.latitude + "," + coffeeShop. ilguma + ") yra" + atstumas + "atstumas antraštėje" + kryptis + "laipsniai."); 

Kodas sukuria išvestį žemiau, o tai rodo, kad kavinė yra tiesiai į vakarus (270,8 laipsnių) nuo mano namo 6,09 atstumu. Vėliau diskutuojama apie atstumo vienetų trūkumą.

 ==================================================== ================= Nuo mano namo (36.538611, -121.7975) iki kavinės (36.539722, -121.907222) yra 6.0873776351893385 atstumas nuo 270.7547022304523 laipsnių. ==================================================== ================= 

Pozicija, „PositionUtility“, o jų kodas yra šiek tiek nerimą keliantis ir tikrai nelabai orientuotas į objektą. Bet kaip taip gali būti? „Java“ yra į objektą orientuota kalba, o kodas naudoja objektus!

Nors kode gali būti naudojami „Java“ objektai, jis tai daro taip, kad primintų praeitą erą: naudingumo funkcijos, veikiančios duomenų struktūrose. Sveiki atvykę į 1972 m. Kai prezidentas Nixonas susigūžė dėl slaptų magnetofonų įrašų, kompiuterių specialistai, koduojantys procedūrine „Fortran“ kalba, sujaudinti naudojo naują Tarptautinę matematikos ir statistikos biblioteką (IMSL) kaip tik tokiu būdu. Kodo saugyklose, tokiose kaip IMSL, buvo gausu skaitinių skaičiavimų funkcijų. Vartotojai šioms funkcijoms perdavė duomenis ilguose parametrų sąrašuose, kuriuose kartais buvo ne tik įvesties, bet ir išvesties duomenų struktūros. (IMSL metams bėgant toliau plėtojosi, o versiją dabar gali įsigyti „Java“ kūrėjai.)

Pagal dabartinį dizainą Pozicija yra paprasta duomenų struktūra ir „PositionUtility“ yra IMSL stiliaus bibliotekos funkcijų saugykla, kuri veikia Pozicija duomenis. Kaip rodo aukščiau pateiktas pavyzdys, šiuolaikinės į objektą orientuotos kalbos nebūtinai trukdo naudoti pasenusias procedūrines technikas.

Duomenų ir metodų susiejimas

Kodas gali būti lengvai patobulintas. Pradedantiesiems, kodėl duomenis ir funkcijas, kurios tuos duomenis veikia, reikia įdėti į atskirus modulius? „Java“ klasės leidžia susieti duomenis ir metodus kartu:

public class Position {public double distance (Position position) {// Apskaičiuokite ir grąžinkite atstumą nuo šio objekto iki nurodytos // padėties. } public double title (Position position) {// Apskaičiuokite ir grąžinkite antraštę iš šio objekto į nurodytą // poziciją. } viešoji dviguba platuma; viešoji dviguba ilguma; } 

Padėties duomenų elementų ir įgyvendinimo kodo apskaičiavimas atstumui ir važiavimui į tą pačią klasę nereikalauja atskiro „PositionUtility“ klasė. Dabar Pozicija pradeda panašėti į tikrą objektu orientuotą klasę. Šiame kode naudojama ši nauja versija, sujungianti duomenis ir metodus:

Pozicija myHouse = nauja pozicija (); myHouse.latitude = 36.538611; myHouse.longitude = -121,797500; Pozicija „coffeeShop“ = nauja pozicija (); coffeeShop.latitude = 36.539722; coffeeShop.longitude = -121.907222; dvigubas atstumas = myHouse.distance (kavos parduotuvė); dviguba antraštė = myHouse.heading (coffeeShop); System.out.println ("Nuo mano namo adresu (" + myHouse.latitude + "," + myHouse.longitude + ") iki kavinės adresu (" + coffeeShop.latitude + "," + coffeeShop.longitude + ") yra atstumas "+ atstumas +", nukreiptas į "+ kryptis +" laipsniai. "); 

Išvestis yra identiška kaip ir anksčiau, o dar svarbiau, kad aukščiau pateiktas kodas atrodo natūralesnis. Ankstesnė versija praėjo du Pozicija prieštarauja atskiros naudingumo klasės funkcijai, kad būtų galima apskaičiuoti atstumą ir kryptį. Šiame kode apskaičiuojant antraštę naudojant metodo iškvietimą util.heading („myHouse“, „coffeeShop“) aiškiai nenurodė skaičiavimo krypties. Kūrėjas turi atsiminti, kad naudingumo funkcija apskaičiuoja antraštę nuo pirmojo parametro iki antrojo.

Palyginimui, aukščiau pateiktame kode naudojamas teiginys „myHouse.heading“ („coffeeShop“) apskaičiuoti tą pačią antraštę. Skambučio semantika aiškiai rodo, kad kryptis eina nuo mano namų iki kavinės. Dviejų argumentų funkcijos konvertavimas antraštė (padėtis, padėtis) prie vieno argumento funkcijos pozicija. antraštė (padėtis) yra žinomas kaip karis funkcija. „Currying“ efektyviai specializuoja funkciją pagal pirmąjį argumentą, todėl aiškesnė semantika.

Metodų pateikimas naudojant Pozicija klasės duomenys Pozicija pati klasė atlieka funkcijų karijavimą atstumas ir Antraštė įmanoma. Tokiu būdu pakeisti funkcijų iškvietimo struktūrą yra reikšmingas pranašumas prieš procedūrines kalbas. Klasė Pozicija dabar reiškia abstraktų duomenų tipą, kuris apima duomenis ir tuos duomenis veikiančius algoritmus. Kaip vartotojo apibrėžtas tipas, Pozicija objektai taip pat yra pirmos klasės piliečiai, kurie naudojasi visais „Java“ kalbos tipo sistemos privalumais.

Kalbos priemonė, sujungianti duomenis su tiems duomenims atliekamomis operacijomis, yra sujungimas. Atkreipkite dėmesį, kad suskaidymas negarantuoja nei duomenų apsaugos, nei informacijos slėpimo. Taip pat kapsulės neužtikrina darnaus klasės dizaino. Norint pasiekti šių kokybės dizaino atributų, reikia metodų, kurie nėra tik kalbos suteikiami. Kaip šiuo metu įgyvendinta, klasė Pozicija nėra nereikalingų ar nesusijusių duomenų ir metodų, bet Pozicija ar atskleidžia abu platuma ir ilguma neapdorota forma. Tai leidžia bet kuriam klasės klientui Pozicija tiesiogiai pakeisti bet kurį vidinį duomenų elementą be jokio ES įsikišimo Pozicija. Akivaizdu, kad kapsulės nepakanka.

Gynybinis programavimas

Jei norite toliau tirti vidinių duomenų elementų atskleidimo pasekmes, tarkime, aš nusprendžiau pridėti šiek tiek gynybinio programavimo Pozicija apribojant platumą ir ilgumą iki GPS nurodytų diapazonų. Platuma patenka į diapazoną [-90, 90], o ilguma - į diapazoną (-180, 180). Duomenų elementų ekspozicija platuma ir ilguma į PozicijaDabartinis įgyvendinimas neleidžia šio gynybinio programavimo.

Padaryti atributus platumą ir ilgumą privatus klasės nariai Pozicija ir pridėjus paprastus prieigos ir mutatoriaus metodus, taip pat paprastai vadinamus „geteriais“ ir „seteriais“, suteikiama paprasta priemonė neapdorotų duomenų elementams atskleisti. Toliau pateiktame kodo pavyzdyje seterio metodai tinkamai patikrina vidines platuma ir ilguma. Užuot metęs išimtį, nurodau atlikti modulio aritmetiką įvesties reikšmėms, kad vidinės vertės išliktų nurodytuose diapazonuose. Pvz., Bandant nustatyti platumą į 181,0, vidinis nustatymas yra –179,0 platuma.

Šis kodas prideda geresnius ir nustatomuosius metodus, kaip pasiekti privačius duomenų narius platuma ir ilguma:

public class Pozicija {public Position (dviguba platuma, dviguba ilguma) {setLatitude (platuma); setLongitude (ilguma); } public void setLatitude (double platitude) {// Užtikrinkite -90 <= platuma <= 90 naudodami modulo aritmetiką. // Kodas nerodomas. // Tada nustatykite egzemplioriaus kintamąjį. tai. platuma = platuma; } public void setLongitude (double longitude) {// Užtikrinkite -180 <ilguma <= 180, naudodami modulo aritmetiką. // Kodas nerodomas. // Tada nustatykite egzemplioriaus kintamąjį. tai.ilguma = ilguma; } public double getLatitude () {return latitude; } public double getLongitude () {grįžimo ilguma; } public double distance (padėties padėtis) {// Apskaičiuokite ir grąžinkite atstumą nuo šio objekto iki nurodytos // padėties. // Kodas nerodomas. } public double title (Position position) {// Apskaičiuokite ir grąžinkite antraštę iš šio objekto į nurodytą // poziciją. } privati ​​dviguba platuma; privati ​​dviguba ilguma; } 

Naudojant aukščiau nurodytą versiją Pozicija reikalingi tik nedideli pakeitimai. Kaip pirmąjį pakeitimą, nes aukščiau pateiktas kodas nurodo konstruktorių, kuriam reikia dviejų dvigubai argumentų, numatytojo konstruktoriaus nebėra. Šiame pavyzdyje naudojamas naujas konstruktorius, taip pat nauji „getter“ metodai. Išvestis išlieka tokia pati kaip pirmame pavyzdyje.

„MyHouse“ padėtis = nauja pozicija (36.538611, -121.797500); Pozicija „coffeeShop“ = nauja pozicija (36.539722, -121.907222); dvigubas atstumas = myHouse.distance (kavos parduotuvė); dviguba antraštė = myHouse.heading (coffeeShop); System.out.println ("Iš mano namo adresu (" + myHouse.getLatitude () + "," + myHouse.getLongitude () + ") iki kavinės adresu (" + coffeeShop.getLatitude () + "," + coffeeShop.getLongitude () + ") yra" + atstumas + "atstumas" + antraštės + "laipsnių."); 

Pasirinkimas apriboti priimtinas platuma ir ilguma taikant seterių metodus yra griežtai priimamas sprendimas dėl projekto. Inkapsuliacija neturi vaidmens. Tai reiškia, kad kapsuliavimas, pasireiškiantis „Java“ kalba, negarantuoja vidinių duomenų apsaugos. Kaip kūrėjas, jūs galite laisvai atskleisti savo klasės vidų. Nepaisant to, turėtumėte apriboti vidinių duomenų elementų prieigą ir modifikavimą naudodami „getter“ ir „setter“ metodus.

Galimų pokyčių išskyrimas

Vidaus duomenų apsauga yra tik viena iš daugelio problemų, skatinančių dizaino sprendimus, be to, kad kalba būtų apibendrinta. Izoliacija pokyčiams yra dar viena. Klasės vidinės struktūros modifikavimas neturėtų, jei tik įmanoma, turėti įtakos klientų klasėms.

Pavyzdžiui, anksčiau pažymėjau, kad atstumas skaičiuojamas klasėje Pozicija nenurodė vienetų. Kad būtų naudinga, nurodytam 6,09 atstumui nuo mano namų iki kavinės aiškiai reikia matavimo vieneto. Galbūt žinau kryptį, kur eiti, bet nežinau, ar nueiti 6,09 metro, nuvažiuoti 6,09 mylių ar nuskristi 6,09 tūkstančius kilometrų.