A InfoWiki wikiből
WikiSysop (vita | szerkesztései)
(Új oldal, tartalma: „<cim cim3="Az adatrejtés és problémái" cim2="Objektum-orientált programozás" cim1="Magasszintű Programozási Nyelvek II." prev="mp3/ea00" next="mp3/ea02" ...”)
Újabb szerkesztés →
A lap 2009. szeptember 3., 20:32-kori változata
Tartalomjegyzék |
Az adatrejtés
Nagyon gyakori, hogy az osztály a mezőit private védelmi szinttel látja el, hogy védje a külvilág elől. Egy ilyen védelmi szintű mezőt a külvilág (osztályon kívüli kód) nem képes sem olvasni, sem írni direkt módon, csak az osztály publikus metódusain keresztül.
Képzeljük el azt az esetet, hogy az objektum-osztályunk egy mobiltelefont modellez le:
class TMobilTelefon { public int PIN = -1; public bool PinEllenorzes( int pinKod) { if (pinKod==PIN) return true; else return false; } public bool PinBeallitas( int regiPinKod, int ujPinKod ) { if (PIN==-1) PIN = ujPinKod; else { if (!PinEllenorzes(regiPinKod)) return false; PIN = ujPinKod; } return true; } }
Nyilván nincs annak semmi értelme, hogy a telefon által tárolt PIN kódot publikussá tegyük, hiszen ekkor a külvilág képes annak értékét kiolvasni, így a telefon PIN kódja bármikor megváltoztatható:
class FoProgram { public static void Main() { TMobilTelefon mob = new TMobiltelefon(); ... mob.PinBeallitas( mob.PIN, 12345 ); } }
Célszerűnek tűnik a kritikus mező elrejtése a külvilág elől:
class TMobilTelefon { private int PIN = -1; ... }
Ekkor a fenti PIN mező nem kiolvasható, így nem alkalmazható a kód megváltoztatása során.
A túlzásba vitt adatrejtés
Nyilván hasonló problémákat okozhat, ha a mobiltelefonunk által használható egységek mennyiségét kell kezelnünk. Ha ezt publikussá tesszük, a külvilág direkt módon feltöltheti a telefont egységekkel telefonkártya-vásárlás nélkül is.
class TMobilTelefon { private int EGYSEGEK_SZAMA; ... }
De ha elrejtjük a külvilág elől ezt a mezőt, akkor annak értékét sem tudjuk kiolvasni:
class FoProgram { public static void Main() { TMobilTelefon mob = new TMobiltelefon(); ... // ez hibás sor, az ´EGYSEGEK_SZAMA´ mező nem elérhető! Console.WriteLine("A telefonon még {0} egység van.",mob.EGYSEGEK_SZAMA); } }
Az adatrejtés szokásos megoldása
A szokásos megoldás, hogy a féltett, kritikus mezőket az osztályok elrejtik a külvilág elől egy korlátozó védelmi szinttel (private vagy protected), de biztosítunk public metódusokat a mező olvasására és írására.
A mező kiolvasását biztosító metódus a névadási hagyományoknak megfelelően get, az író metódust set formában szoktuk elnevezni:
class TMobilTelefon { private int EgysegekSzama; public int getEgysegekSzama() { return EgysegekSzama; } public void setEgysegekSzama( int ujEgysegekSzama ) { if (ujEgysegekSzama>0) EgysegekSzama = ujEgysegekSzama; else throw new Exception("Negatív szám nem megengedett!"); } }
A get függvény visszatérési típusa megegyezik a szóban forgó mező típusával, és nem fogad paramétert értelemszerűen. A metódus egyszerűen visszaadja a keresett mező értékét mint visszatérési érték. Amennyiben a mező értéke nem kiolvasható (mint amilyen a feljebb lévő PIN mező), úgy ezt a metódust egyszerűen nem írjuk meg.
A set metódus jellemzően egy paramétert fogad: a mező új értékét. A metódus megvizsgálja a paraméterben kapott új értéket, általában egy if-el, vagy egyéb módon. Amennyiben az értéket megfelelőnek találja - úgy eltárolja a mezőben. Ha hibás vagy értelmetlen értéket kapna - úgy megtagadja az érték tárolását. Ezt OOP világába egy kivétel feldobásával szoktuk jelezni.
A PROPERTY
A fenti megoldással az objektum biztosítja a külvilág felé az elrejtett mező irányában történő olvasás és írás lehetőségét - ellenőrzött módon. Az objektum a mezőbe történő érték eltárolás előtt lehetőséget kap annak ellenőrzésére, felülvizsgálatára.
A get és set függvények használatának egyetlen hátránya, hogy a felhasználói kód elég csúnya. Első ránézésre nem is látszik látványosan, hogy valójában egyszerűen egy mezőt írunk és olvasunk:
class FoProgram { public static void Main() { TMobilTelefon mob = new TMobiltelefon(); ... // telefonkártya feltöltése mob.setEgysegekSzama( mob.getEgysegekSzama() + 2000 ); } }
A property (magyarra fordítva jellemző, tulajdonság) ezen próbál segíteni. A property egy virtuális mező. Tréfásan ezt akár mezőnek látszó izé-nek is lehetne hívni:
class TMobilTelefon { private int _EgysegekSzama; public int EgysegekSzama { get { return _EgysegekSzama; } set { if (value>0) _EgysegekSzama = value; else throw new Exception("Negatív szám nem megengedett!"); } } }
A fenti példában az EgysegekSzama nem metódus, mert akkor gömbölyű zárójelek állnának mögötte a formális paraméterlista leírása végett:
public int EgysegekSzama() { ... }
Az EgysegekSzama nem lehet mező sem, mert akkor a deklarációs sort egy pontosvessző zárná le, és nem lenne saját blokkja a { } jelek között.
public int EgysegekSzama;
A fenti példában szereplő EgysegekSzama egy property. A property-nek van típusa, valamint törzse, mely két részre osztható: get és set részre.
A property-t a külvilág azzal a szintakszissal használja, mintha az ténylegesen létező mező lenne:
class FoProgram { public static void Main() { TMobilTelefon mob = new TMobiltelefon(); ... // telefonkártya feltöltése int x = mob.EgysegekSzama; // olvasás mob.EgysegekSzama = x + 2000 ; // írás } }
A get rész akkor hajtódik végre, amikor a property értékét a külső kód ki szeretné olvasni. A get-hez tartozó saját utasításblokk belsejében szereplő return-nek kell az értéket meghatározni. A return után írt kifejezés típusa természetesen meg kell egyezzen a property típusával, hiszen a külső kód ilyen típusú értéket vár. Ennek megfelelően a get rész egy beágyazott függvénynek is felfogható.
A set rész akkor hajtódik végre, amikor a property-be a külső kód értéket kíván írni. A set utasításblokkjában az elhelyezendő értéket a value tartalmazza. A value itt most egy konstansnak tekinthető, melynek típusa a property típusával egyezik meg.
A set utasításblokkjában a value-ben lévő értéket le szoktuk ellenőrízni, mielőtt ténylegesen befogadjuk. Ha az érték nem megfelelő, akkor a szokásos eljárás, hogy kivételt dobunk fel, ezzel jelezzük az elutasítást.
Csak írható property
A property törzse általában két részre bomlik: get és set. Ugyanakkor nem kötelező mindkét részt megírni. Amennyiben nem dolgozzuk ki a get, csak a set részt, úgy a property olvasását nem tesszük lehetővé a külvilág számára, csak az írást. Így egy csak írható property-t tudunk előállítani:
class TMalacPersely { public int _penz; public int penz { set { if (value>0) _penz += value; else throw new Exception("Nem csökkentheted " "a perselyben levő pénz mennyiségét!"); } } }
Csak olvasható property
A csak írható property mintájára elkészíthető a csak olvasható property is. Ez csak get szakaszt tartalmaz, így értéket nem helyezhetünk el benne, csak kiolvashatjuk annak értékét.
Az alábbi példa egy olyan property-t mutat be, amely mögött nincs tároló mező, hanem a kiolvasandó értéket on-line generáljuk:
class TKor { public double sugar; public double Kerulet { get { return 2*sugar*Math.Pi; } } }
Az adatrejtés problémaköre
Az alapprobléma az, hogy az osztály mezőinek értékére általában szabály van felírva. Ezen szabály azt azonosítja, hogy mely esetekben milyen értékeket vehet fel a mező ahhoz, hogy az objektum ne kerüljön ellentmondásba.
Például ilyen szabály az, ha egy Kor osztály sugar mezője nem vehet fel negatív vagy nulla értéket, mivel ilyen sugarú köröket a grafikus képernyőn nem tudunk megjeleníteni, értelmes módon.
Az objektumosztály megalkotásakor ezen szabályok folyamatos betartásáról saját magának kell gondoskodnia. Nem várható el az, hogy a főprogram ezen szabályokat egyébként ismerje, és nem hihetjük azt el, hogy a főprogram ezen szabályokat nem fogja megszegni.
Ha egy objektumosztály egy ilyen szabályt nem megfelelően tartat be, és e miatt valamely metódusa hibás működést produkál, akkor minden esetben az osztályt implementáló programozó a hibás!
Milyen esetek képzelhetőek el?
- A mezőre semmilyen szabály nincs alkotva, értéke tetszőleges lehet. Ezen mező a külvilág számára szabadon, kontroll nélkül írható / olvasható, public.
- A mező a külvilág felé csak olvasható. Ezt általában egy private vagy protected mezővel, és egy csak get részt tartalmazó property-vel szoktuk megoldani.
- A mező a külvilág felé olvasható és írható, de a szabály betartása mellett. Ezt általában egy private vagy protected mezővel, és egy get és set részt is tartalmazó property-vel szoktuk megoldani, ahol a szabály leprogramozása a set részben történik meg.
Nyelvi különbségek
Nem minden OOP nyelv támogatja a property-k készítését. Amely nyelvven ez nem támogatott, úgy marad az eredeti megoldás: set és get metódusok készítése. Amely nyelven azonban ez támogatott, úgy érdemes ezt a megoldást használni, hiszen nem jelent többletmunkát az osztály készítőjének, de a felhasználói oldalon elegánsabb, jobban olvasható kód megírását teszi lehetővé.
Ugyanakkor le kell szögeznünk, hogy a felhasználói oldalt a property könnyen megtévesztheti. A felhasználói oldalon ugyanis nem könnyű megkülönböztetni a property-t a ténylegesen létező mezőktől, mivel mindkettőt ugyanazzal a szintaxissal kell használnia. De amíg a ténylegesen létező mező olvasása és írása direkt módon, nagy sebességgel történik, addig a property írása és olvasása minden esetben függvényhívással jár. Ez mindenképpen lassúbb, mint a mező esetén.