Programovanie

Zapuzdrenie nie je skrývanie informácií

Slová sú klzké. Rovnako ako Humpty Dumpty vyhlásený u Lewisa Carrolla Cez zrkadlo „Keď použijem slovo, znamená to presne to, čo som si vybral, že to znamená - ani viac, ani menej.“ Určite bežné používanie týchto slov zapuzdrenie a skrývanie informácií Zdá sa, že sleduje túto logiku. Autori zriedka rozlišujú medzi týmito dvoma typmi a často priamo tvrdia, že sú rovnaké.

Je to tak? Nie je pre mňa. Keby to bola iba záležitosť slov, nenapísal by som k tomu ďalšie slovo. Za týmito pojmami sa však skrývajú dva odlišné koncepty, a to koncepty vytvorené zvlášť a najlepšie pochopiteľné osobitne.

Zapuzdrenie označuje spojenie údajov s metódami, ktoré na týchto údajoch fungujú. Táto definícia je často nesprávne interpretovaná, čo znamená, že údaje sú nejako skryté. V Jave môžete mať zapuzdrené údaje, ktoré nie sú vôbec skryté.

Skrytie údajov však nie je úplným rozsahom skrývania informácií. David Parnas prvýkrát predstavil koncept skrývania informácií okolo roku 1972. Tvrdil, že primárne kritériá pre modularizáciu systému by sa mali týkať skrývania kritických rozhodnutí o dizajne. Zdôraznil, že sa skrývajú „ťažké rozhodnutia o dizajne alebo rozhodnutia o dizajne, ktoré sa pravdepodobne zmenia“. Skrytie informácií týmto spôsobom izoluje klientov od požadovania dôkladných znalostí o dizajne použitia modulu a od účinkov zmeny týchto rozhodnutí.

V tomto článku skúmam rozdiel medzi zapuzdrením a informáciami skrývajúcimi sa pri vývoji ukážkového kódu. Diskusia ukazuje, ako Java uľahčuje zapuzdrenie, a skúma negatívne dôsledky zapuzdrenia bez skrývania údajov. Príklady tiež ukazujú, ako vylepšiť dizajn triedy pomocou princípu skrývania informácií.

Trieda polohy

S rastúcim povedomím o obrovskom potenciáli bezdrôtového internetu mnohí experti očakávajú, že služby založené na polohe poskytnú príležitosť pre prvú aplikáciu zabijáka bezdrôtových sietí. Pre ukážkový kód tohto článku som vybral triedu predstavujúcu geografické umiestnenie bodu na zemskom povrchu. Ako entita domény je trieda s názvom Pozícia, predstavuje informácie o globálnom pozičnom systéme (GPS). Prvý rez v triede vyzerá tak jednoducho, ako:

public class Pozícia {public double latitude; verejná dvojnásobná zemepisná dĺžka; } 

Trieda obsahuje dve dátové položky: GPS zemepisná šírka a zemepisná dĺžka. V súčasnosti Pozícia nie je nič iné ako malá taška s dátami. Avšak, Pozícia je trieda a Pozícia objekty môžu byť inštancované pomocou triedy. Na využitie týchto objektov, trieda PositionUtility obsahuje metódy na výpočet vzdialenosti a smeru - teda smeru - medzi zadanými Pozícia objekty:

verejná trieda PositionUtility {verejná statická dvojnásobná vzdialenosť (Pozícia polohy1, Pozície polohy2) {// Vypočíta a vráti vzdialenosť medzi zadanými pozíciami. } verejná statická dvojitá hlavička (Pozícia pozícia1, Pozícia pozícia2) {// Vypočítajte a vráťte nadpis z polohy1 do polohy2. }} 

Skutočný implementačný kód pre výpočty vzdialenosti a smeru vynechávam.

Nasledujúci kód predstavuje typické použitie Pozícia a PositionUtility:

// Vytvorenie Pozície predstavujúcej môj dom Pozícia myHouse = nová Pozícia (); myHouse.latitude = 36.538611; myHouse.longitude = -121,797500; // Vytvorenie Pozície predstavujúcej miestnu kaviareň Pozícia coffeeShop = nová Pozícia (); coffeeShop.latitude = 36.539722; coffeeShop.longitude = -121,907222; // Pomocou nástroja PositionUtility vypočítajte vzdialenosť a smer z môjho domu // do miestnej kaviarne. dvojitá vzdialenosť = PositionUtility.distance (myHouse, coffeeShop); dvojitý nadpis = PositionUtility.heading (myHouse, coffeeShop); // Výsledky tlače System.out.println ("Z môjho domu v (" + myHouse.latitude + "," + myHouse.longitude + ")) do kaviarne v (" + coffeeShop.latitude + "," + coffeeShop. zemepisná dĺžka + ") je vzdialenosť" + vzdialenosť + "pri smerovaní" + smerovanie + "stupňov."); 

Kód generuje výstup uvedený nižšie, čo naznačuje, že kaviareň je na západ (270,8 stupňov) od môjho domu vo vzdialenosti 6,09. Neskoršia diskusia sa bude zaoberať nedostatkom jednotiek vzdialenosti.

 ================================================== ================== Od môjho domu v (36.538611, -121,7975) do kaviarne v (36.539722, -121,907222) je vzdialenosť 6,0873776351893385 pod číslom 270,7547022304523 stupňov. ================================================== ================== 

Pozícia, PositionUtilitya ich využitie kódu sú trochu znepokojujúce a rozhodne nie sú príliš objektovo zamerané. Ale ako to môže byť? Java je objektovo orientovaný jazyk a kód používa objekty!

Hoci kód môže používať objekty Java, robí to spôsobom, ktorý pripomína minulú dobu: pomocné funkcie fungujúce na dátových štruktúrach. Vitajte v roku 1972! Keď sa prezident Nixon schúlil k tajným páskovým nahrávkam, počítačoví profesionáli kódujúci procedurálny jazyk Fortran s nadšením využívali novú Medzinárodnú knižnicu matematiky a štatistiky (IMSL) práve týmto spôsobom. Úložiská kódov, ako napríklad IMSL, boli plné funkcií pre numerické výpočty. Používatelia odovzdávali údaje týmto funkciám v dlhých zoznamoch parametrov, ktoré občas zahŕňali nielen vstupné, ale aj výstupné dátové štruktúry. (IMSL sa v priebehu rokov neustále vyvíjal a vývojári Java majú teraz k dispozícii verziu.)

V súčasnom dizajne Pozícia je jednoduchá dátová štruktúra a PositionUtility je archív funkcií knižnice v štýle IMSL, ktorý pracuje ďalej Pozícia údaje. Ako ukazuje vyššie uvedený príklad, moderné objektovo orientované jazyky nevyhnutne nevylučujú použitie zastaraných procedurálnych postupov.

Zber dát a metódy

Kód je možné ľahko vylepšiť. Prečo by ste mali na začiatku umiestniť údaje a funkcie, ktoré na týchto údajoch pracujú, do samostatných modulov? Triedy Java umožňujú spájanie údajov a metód dohromady:

public class Position {public double distance (Position position) {// Vypočítajte a vráťte vzdialenosť od tohto objektu k určenej // pozícii. } verejná dvojitá hlavička (Pozícia polohy) {// Vypočíta a vráti nadpis z tohto objektu na určenú // pozíciu. } verejná dvojitá šírka; verejná dvojnásobná zemepisná dĺžka; } 

Uvedenie polohových údajových položiek a implementačného kódu na výpočet vzdialenosti a smeru do rovnakej triedy odstraňuje potrebu samostatného použitia PositionUtility trieda. Teraz Pozícia sa začína podobať na skutočnú objektovo orientovanú triedu. Nasledujúci kód používa túto novú verziu, ktorá spája údaje a metódy dohromady:

Pozícia myHouse = nová pozícia (); myHouse.latitude = 36.538611; myHouse.longitude = -121,797500; Pozícia coffeeShop = nová Pozícia (); coffeeShop.latitude = 36.539722; coffeeShop.longitude = -121,907222; dvojitá vzdialenosť = myHouse.distance (coffeeShop); dvojitý nadpis = myHouse.heading (coffeeShop); System.out.println ("Z môjho domu na (" + myHouse.latitude + "," + myHouse.longitude + ") do kaviarne na adrese (" + coffeeShop.latitude + "," + coffeeShop.longitude + ") je vzdialenosť „+ vzdialenosť +“ pri smerovaní „+ smerovanie +„ stupňov. “); 

Výstup je identický ako predtým, a čo je dôležitejšie, vyššie uvedený kód sa zdá byť prirodzenejší. Predchádzajúca verzia prešla dvoma Pozícia objekty na funkciu v samostatnej triede úžitkových vlastností na výpočet vzdialenosti a smeru. V tomto kóde sa počíta hlavička s volaním metódy util.heading (myHouse, coffeeShop) jasne neoznačoval smer výpočtu. Vývojár si musí pamätať, že obslužná funkcia vypočíta nadpis od prvého parametra k druhému.

Na porovnanie, vyššie uvedený kód používa vyhlásenie myHouse.heading (coffeeShop) vypočítať rovnakú hlavičku. Sémantika výzvy jasne naznačuje, že smerovanie postupuje z môjho domu do kaviarne. Prevod funkcie dvoch argumentov nadpis (Pozícia, Pozícia) na funkciu jedného argumentu position.heading (Pozícia) je známy ako kari funkcia. Currying efektívne špecializuje funkciu na prvý argument, čo vedie k jasnejšej sémantike.

Umiestnenie použitých metód Pozícia údaje triedy v Pozícia trieda sama robí kari funkcie vzdialenosť a nadpis možné. Zmena štruktúry hovorov funkcií týmto spôsobom je oproti procedurálnym jazykom významnou výhodou. Trieda Pozícia teraz predstavuje abstraktný dátový typ, ktorý zapuzdruje údaje a algoritmy, ktoré na nich pracujú. Ako typ definovaný používateľom Pozícia objekty sú tiež občanmi prvej triedy, ktorí využívajú všetky výhody systému jazykových typov Java.

Jazykovým prostriedkom, ktorý spája údaje s operáciami, ktoré s týmito údajmi pracujú, je zapuzdrenie. Zapamätanie nezaručuje ochranu údajov ani skrytie informácií. Ani zapuzdrenie nezabezpečuje súdržný dizajn triedy. Na dosiahnutie týchto atribútov kvalitného dizajnu sú potrebné techniky prekračujúce zapuzdrenie poskytované jazykom. Ako je v súčasnosti implementované, trieda Pozícia neobsahuje nadbytočné alebo nesúvisiace údaje a metódy, ale Pozícia vystavuje oboje zemepisná šírka a zemepisná dĺžka v surovej forme. To umožňuje každému klientovi triedy Pozícia priamo zmeniť buď internú údajovú položku bez zásahu používateľa Pozícia. Je zrejmé, že zapuzdrenie nestačí.

Defenzívne programovanie

Ak chcem ďalej preskúmať dôsledky odhalenia interných dátových položiek, predpokladám, že sa rozhodnem pridať trochu obranného programovania Pozícia obmedzením zemepisnej šírky a dĺžky na rozsahy určené GPS. Zemepisná šírka spadá do rozsahu [-90, 90] a zemepisná dĺžka do rozsahu (-180, 180). Expozícia dátových položiek zemepisná šírka a zemepisná dĺžka v PozíciaVďaka súčasnej implementácii je toto obranné programovanie nemožné.

Vytváranie atribútov zemepisná šírka a dĺžka súkromné dátoví členovia triedy Pozícia a pridanie jednoduchých metód prístupového a mutačného nástroja, tiež bežne nazývaných getre a setre, poskytuje jednoduchý prostriedok na odhalenie nespracovaných dátových položiek. V príkladovom kóde uvedenom nižšie metódy nastavovača vhodne skrínujú interné hodnoty zemepisná šírka a zemepisná dĺžka. Namiesto toho, aby som hodil výnimku, špecifikujem vykonanie modulo aritmetiky na vstupných hodnotách, aby sa interné hodnoty udržali v zadaných rozsahoch. Napríklad pokus o nastavenie zemepisnej šírky na 181,0 má za následok interné nastavenie na -179,0 pre zemepisná šírka.

Nasledujúci kód pridáva metódy getra a setra pre prístup k členom súkromných údajov zemepisná šírka a zemepisná dĺžka:

verejná trieda Pozícia {verejná pozícia (dvojitá zemepisná šírka, dvojitá zemepisná dĺžka) {setLatitude (zemepisná šírka); setLongitude (longitude); } public void setLatitude (double latitude) {// Zaistite -90 <= šírka <= 90 pomocou aritmetiky modulo. // Kód sa nezobrazuje. // Potom nastavíme premennú inštancie. this.latitude = latitude; } public void setLongitude (double longitude) {// Zaistite -180 <zemepisná dĺžka <= 180 pomocou aritmetiky modulo. // Kód sa nezobrazuje. // Potom nastavíme premennú inštancie. this.longitude = longitude; } public double getLatitude () {return latitude; } public double getLongitude () {return longitude; } verejná dvojitá vzdialenosť (Pozícia polohy) {// Vypočíta a vráti vzdialenosť od tohto objektu k určenej // pozícií. // Kód sa nezobrazuje. } verejná dvojitá hlavička (Pozícia polohy) {// Vypočíta a vráti nadpis z tohto objektu na určenú // pozíciu. } dvojitá súkromná šírka; súkromná dvojnásobná zemepisná dĺžka; } 

Pomocou vyššie uvedenej verzie Pozícia vyžaduje iba malé zmeny. Ako prvá zmena, pretože vyššie uvedený kód určuje konštruktor, ktorý trvá dva dvojitý argumenty, predvolený konštruktor už nie je k dispozícii. Nasledujúci príklad používa nový konštruktor, ako aj nové metódy getra. Výstup zostáva rovnaký ako v prvom príklade.

Pozícia myHouse = nová pozícia (36.538611, -121,797500); Pozícia coffeeShop = nová pozícia (36.539722, -121,907222); dvojitá vzdialenosť = myHouse.distance (coffeeShop); dvojitý nadpis = myHouse.heading (coffeeShop); System.out.println ("Z môjho domu na adrese (" + myHouse.getLatitude () + "," + myHouse.getLongitude () + ") do kaviarne na adrese (" + coffeeShop.getLatitude () + "," + coffeeShop.getLongitude () + ") je vzdialenosť" + vzdialenosť + "pri nadpise" + nadpis + "stupňov."); 

Rozhodnutie obmedziť prípustné hodnoty zemepisná šírka a zemepisná dĺžka metódami setra je striktne rozhodnutie o dizajne. Zapuzdrenie nehrá rolu. To znamená, že zapuzdrenie, ktoré sa prejavuje v jazyku Java, nezaručuje ochranu interných údajov. Ako vývojár môžete odhaliť vnútorné časti svojej triedy. Napriek tomu by ste mali obmedziť prístup a úpravy interných údajových položiek pomocou metód getra a setra.

Izolačná zmena potenciálu

Ochrana interných údajov je len jedným z mnohých problémov, ktoré vedú k rozhodovaniu o dizajne popri jazykovej enkapsulácii. Izolácia na zmenu je iná. Úpravy vnútornej štruktúry triedy by nemali mať, pokiaľ je to možné, vplyv na triedy klientov.

Napríklad som predtým poznamenal, že výpočet vzdialenosti v triede Pozícia neoznačoval jednotky. Aby to bolo užitočné, uvádzaná vzdialenosť 6,09 od môjho domu po kaviareň jednoznačne potrebuje mernú jednotku. Možno poznám smer, ktorým sa mám uberať, ale neviem, či ísť 6,09 metra, jazdiť 6,09 míle alebo letieť 6,09-tisíc kilometrov.

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