Objektumleírók
Konstantinusz Kft. © 2010
1. Tartalomjegyzék 1. 2. 3. 4. 5.
Tartalomjegyzék ...................................................................................................................... 2 Mi az a leíró? ........................................................................................................................... 2 Közvetett paraméter átadások ................................................................................................. 4 Absztrakt metódusok, absztrakt konstrukció ............................................................................ 7 Validálás leíróval .................................................................................................................... 10
2. Mi az a leíró? Vegyünk egy egyszerű példát egy osztályra és annak a konstruktorára, egy C-szerű fiktív nyelven.
class Szemely{ string nev,cim,telefon;
public Szemely(string nev, string cim, string telefon){ this.nev=nev; this.cim=cim; this.telefon=telefon; }
}
Most pedig, ahelyett, hogy a konstruktor paramétereit külön-külön szerepeltetnénk, mint formális paramétereket, definiáljunk egy adattípust, ami ugyanazokat a mezőket tartalmazza, mint az adott metódus formális paraméterei. Ezt a struktúrát fogjuk leírónak nevezni. Ezek után a metódus nem a paramétereket kéri be egyenként, hanem egy leírót.
2/12
struct Szemely_leiro { string nev, cim, telefon; }
class Szemely{ string nev,cim,telefon;
public Szemely(Szemely_leiro leiro){ this.nev=leiro.nev; this.cim=leiro.cim; this.telefon=leiro.telefon; }
}
Eddig látszólag nem történt semmi különös, szóval mégis mire jó ez? A továbbiakban nézzünk meg néhány specifikus esetet, amin keresztül megérthetjük ennek a módszernek a hasznát.
3/12
3. Közvetett paraméter átadások Tegyük fel, hogy a fent látott Szemely osztálynál kötelező kitölteni a nevet és a címet, és azt szeretnénk, hogy addig ne is lehessen létrehozni egy Szemely példányt, amíg a paraméterek nincsenek rendben. Ehhez mindenképp kell még a konstruktor előtt valamilyen ellenőrzés, más néven validálás, hiszen mire a konstruktorba írt kód lefut, addigra már késő, a példány létrejött. Tehát kell egy osztálymetódus, ami bekéri ugyanazokat a paramétereket, mint a konstruktor, leellenőrzi őket, és a minden rendben, akkor példányosítja az objektumot, és visszaadja a példányt. Az ilyen metódusokat hívják “factory” metódusnak, mivel objektumok “gyártását” végzik. Most lássuk először a hagyományos paraméterezéssel. class Szemely{ string nev,cim,telefon; public static bool validalas(string nev, string cim, string telefon){ if (nev==”” or cim==””) return false;
return true; }
public static Szemely letrehoz(string nev, string cim, string telefon){ if (!validalas(nev, cim, telefon))
return null;
return new Szemely(nev, cim, telefon); }
protected Szemely(string nev, string cim, string telefon){ this.nev=nev; this.cim=cim; this.telefon=telefon; } }
4/12
Itt a konstruktor nem publikus, tehát kívülről nem konstruálható közvetlenül ez az osztály, muszáj a letrehoz() metódust használni. Viszont itt máris látható egy kellemetlenség. A nev, cim, telefon paramétereket a letrehoz() metódus, illetve a validalas() metódus formális paraméter listájában is szerepeltetni kell, és át is kell adni őket, egymásnak és a konstruktornak. Ez a helyzet problémás redundanciákat okozhat. Ötször kell leírnom ugyanazt a paraméter listát. Ez fölösleges, és ráadásul amikor egyik metódus átadja a másiknak, akkor még a jó sorrendre is figyelni kell, nehogy összekeveredjenek. Gondoljuk el, mi lenne, ha a paraméterlistát változtatni kell, vagyis bővíteni, és/vagy kivenni belőle dolgokat. Ekkor még jobb esetben is öt helyen kell átírni, és nem elrontani a sorrendeket. Ebben a helyzetben már sokkal vonzóbbnak tűnik a leírós megoldás: class Szemely{ string nev,cim,telefon; public static bool validalas(Szemely_leiro leiro){ if (leiro.nev==”” or leiro.cim==””) return false; return true; }
public static Szemely letrehoz(Szemely_leiro leiro){ if (!validalas(leiro)) return null;
return new Szemely(leiro); }
protected Szemely(Szemely_leiro leiro){ this.nev=leiro.nev; this.cim=leiro.cim; this.telefon=leiro.telefon; }
}
5/12
Figyeljük meg, mennyivel szebb és egyszerűbb lett a kód. Itt már ha változtatni akarok a paraméter listán, akkor nem kell számtalan helyen átírnom, és nem kell a paramétereket egyesével átadogatni a metódusok között. Ha lenne egy tucat mezője a leírónak, a legtöbb helyen akkor is csak a leírót adom át, így teljesen átlátható marad a kód.
Általánosságban tehát azt mondhatjuk, hogy a leírók paraméter listákat fognak össze egy csomagba, ezzel a csomag könnyen “hordozhatóvá” válik, ráadásul a metódusok, amik között átadogatjuk, nem is kell hogy ismerjék a tartalmát. Figyeljük meg, hogy a letrehoz() metódus semmit nem kell hogy foglalkozzon a leíró tartalmával, nem kell tudnia róla hogy mi van benne, csak közvetítenie kell a validalas() metódus és a konstruktor között. A leíróknak ez a tulajdonsága pedig egészen új, érdekes lehetőségeket nyit meg előttünk.
6/12
4. Absztrakt metódusok, absztrakt konstrukció Leírók kapóra jönnek amikor az ember absztrakt osztályokkal, és erős polimorfizmussal dolgozik. Előfordulnak olyan helyzetek, amikor olyan kódot kell írni, ami paraméterként kapott ismeretlen szerkezetű információk alapján ismeretlen osztályú absztrakt objektumok metódusait kell hogy használja. Absztrakt metódus interfésze (formális paraméterlista, stb.) az azt implementáló alosztályokban ugyanaz kell hogy maradjon. Viszont előfordulhat, hogy alosztályonként más kellene hogy legyen ennek a metódusnak a paraméter listája. Ez főleg akkor történhet meg, ha ez a metódus valamilyen másik osztályt példányosít, aminek a konstruktorához szükséges információt ő maga is paraméterként kéri be. Ekkor a különböző implementációk más paraméterlistát igényelhetnének, mivel más fajta objektumokat akarnak konstruálni. Ez hagyományos módon nem lehetséges, már eleve azért sem, mert ez nyelvi szinten ellentmondásos lenne. De szerencsére leírók használatával van egy kiskapunk.
Most nézzünk erre a szituációra egy példát. Lesz egy műhelyünk ami mindenféle típusú autókat szerel össze, majd végül leteszteli a motort, a féket, és kifesti a megadott színre. A trükk az, hogy a különféle autótípusok összeszerelése egyedi módon történik, egyedi alkatrészekből. Ezért, hogy ne a műhelynek kelljen tudnia, hogy hogyan kell minden egyes típust összeszerelni, autószerelők vannak, akik értenek egy-egy típus összeszereléséhez. Így a műhely csak megkapja egy nagy konténerben az alkatrészeket, amikről nem is kell tudnia semmit, átadja a megfelelő szerelőnek, az összeszereli, a műhely pedig elvégzi a teszteket, és az utómunkát.
abstract class Auto_leiro{ }
abstract class Auto{ Szin szin;
void motorTeszt(){ //motor tesztelési kódja.. } void fekTeszt(){ //fék tesztelési kódja.. }
7/12
void kifest(Szin szin){ this.szin=szin; } }
abstract class AutoSzerelo{ abstract Auto osszeszerel(Auto_leiro alkatreszek); }
class Muhely{
public Auto autoOsszeszerel(AutoSzerelo szerelo, Auto_leiro alkatreszek, Szin szin) { Auto auto; auto=szerelo.osszeszerel(alkatreszek);
auto.motorTeszt(); auto.fekTeszt(); auto.kifest(szin);
return Auto; } }
Ebben az esetben annyival tovább léptünk, hogy már leíró is egy objektum, nem csak egy sima adatszerkezet. Így neki is lehetnek alosztályai, absztrakt tagjai stb. Láthatjuk hogy az autót, és az autószerelőt is egy absztrakt osztály képviseli, amiből majd a konkrét autó típusok, és a hozzájuk tartozó szakértő szerelők fognak származni. Az AutoSzerelo osztály ebben az esetben egy úgynevezett “factory” szerepét tölti be, vagyis egy olyan objektum, ami más osztályok 8/12
példányosítását végzi. Ebben az esetben az AutoSzerelo osztály osszeszerel metódusa ugyanaztteszi, mint korábban a Szemely osztály letrehoz() metódusa. Ide is ugyanúgy lehetne validálást is beépíteni, mint ahogy azt fent láttuk. A lényeg, hogy minden AutoSzerelo alosztály ebben a metódusban példányosítja a megfelelő osztályú autót, és az ehhez szükséges információt egy leírón keresztül kapja, amit akár egyenesen tovább is adhat az autó konstruktorának anélkül hogy “kibontaná”, hiszen az autó konstruktora is működhet leíróval.
Így végül, mint láthatjuk, a Muhely autoOsszeszerel() metódusa teljesen “vakon” tud autókat gyártani, anélkül hogy tudná, hogy mit gyárt és miből. Ezt hívjuk absztrakt konstrukciónak. Ezek a helyzetek leginkább akkor fordulnak elő, ha valamilyen plugin-ezhető rendszert kell készíteni. Például ebben az esetben a különböző autó típusok, az alkatrészeik, és a szerelőik működhetnének plugin-ként.
9/12
5. Validálás leíróval
Végül jöjjön a leíróknak egy érdekes alkalmazása. Láttuk korábban, hogy objektumok konstruálása valamilyen ellenőrzésen megy át először, ezért használunk validáló metódusokat, amik vagy az adott osztály statikus metódusai, vagy egy factory példány metódusai. Viszont, ha az ember leírót használ, akkor a validálást fel lehet fogni úgy is, hogy egy adott paraméter listáról szeretném eldönteni,hogy valid-e vagy sem. Ha pedig valid, akkor fel lehet használni. Ebben az elgondolásban úgy tűnik mintha a validálás, illetve validitás magához a paraméter listához kötődne. Márpedig ha a leíró egy paraméter listát testesít meg, akkor ezek a leírónak a tulajdonságai, illetve viselkedése, vagyis a leíró validálja önmagát.
Konkrétabban fogalmazva, a korábban látott validáló metódusok átköltözhetnek a leíróba, persze feltéve, hogy a leíró egy objektum. Nézzük meg, hogyan változik ezzel a Szemely és Szemely_leiro példakódunk:
class Szemely_leiro { string nev, cim, telefon; bool valid=false;
Szemely_leiro(string nev, string cim, string telefon){ this.nev=nev; this.cim=cim; this.telefon=telefon; }
bool validalas(){ //ha már validak vagyunk akkor nem kell újra ellenőrzés if (this.valid) return true;
if (this.nev==”” or this.cim==””)
10/12
return false;
//beállítjuk a leíró állapotát validra this.valid=true; return true; } }
class Szemely{ string nev,cim,telefon;
public static Szemely letrehoz(Szemely_leiro leiro){ //itt már a leírótól kérdezzük meg, hogy valid-e if (!leiro->validalas()) return null;
return new Szemely(leiro); }
protected Szemely(Szemely_leiro leiro){ this.nev=leiro.nev; this.cim=leiro.cim; this.telefon=leiro.telefon;; }
}
Ezt a fajta leírót akár előre be is validálhatom, és amikor átadom a letrehoz() metódusnak, akkor a validálási folyamat nem fog még egyszer lefutni.
11/12
Leírókkal ennél messzebbre is lehet menni. Például lehet beépíteni automatikus validálásokat olyan dolgokra, amiket gyakran kell vizsgálni (string üres-e, számmá konvertálható-e, stb). Lehet alapértelmezett értékek incializálását is végezni, lehet automatikus hibaüzeneteket generálni.
Mindenképp érdemes élni ezzel a módszerrel, ha mást nem akkor csak a kód egyszerűsítése céljából.
12/12