Ž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
į Pozicija
Dabartinis į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ų.