Programozás II gyakorlat 7. Példák a polimorfizmus alkalmazásaira
Probléma class A { public: ~A() { cout << "A destruktora" << endl; ; class B: public A { public: ~B() { cout << "B destruktora" << endl; ; int main() { A * a = new B; delete a; Melyik destruktor hívódik meg? 2
Probléma Az a változó A * típusú A fordító azt hiszi, hogy egy A típusú objektumot kell felszabadítani Csak az A osztály destruktora hívódik meg Ha a B osztálynak is fel kell szabadítania erőforrásokat, akkor probléma lép fel Tegyük virtuálissá a destruktort! 3
Virtuális destruktor class A { public: virtual ~A() { cout << "~A" << endl; ; class B: public A { public: ~B() { cout << "~B" << endl; ; int main() { A * a = new B; delete a; Először a B destruktora fut le, majd az meghívja az ős destruktorát is 4
Feladat Egy olyan szoftvert fejlesztünk, amelynek fájlba kell mentenie a különböző eseményeket, illetve meg is kell jelenítenie azokat 3 fajta esemény van: általános esemény, rendszerhiba, és figyelmeztetés, minden fajta eseményt ugyanabba a fájlba mentünk A szoftver megrendelője azt szeretné, hogy ezt a naplót két fajta formátumban lehessen elmenteni, és megjeleníteni 5
Feladat A támogatandó fájlformátumok: Egyszerű szöveges fájl: A fájl minden sora egy eseményt tartalmaz, és egy kódot (0-2-ig), amely jelzi az esemény típusát Ha ezt meg akarjuk jeleníteni a szoftverben, akkor fekete karakterekkel jelenítünk meg eseményt 6
Feladat A támogatandó fájlformátumok: HTML fájl: Egy weboldalként kell elmenteni a naplót, ahol egy sor egy eseményt jelez A sor fekete karakterekből áll, ha sima eseményről van szó A figyelmeztetés színe a sárga A rendszerhiba színe a piros A szoftverben ezt a naplót színekkel együtt kell megjeleníteni 7
Tervezés: Builder (építő) minta Szükség lesz egy osztályra, amely képes kezelni a naplót Két különböző formátumot kell kezelni Célszerű a két formátumra két osztályt írni: HtmlLog és TextLog A két osztály származzon a Log ősosztályból Ezen osztályokat lássuk el virtuális függvényekkel 8
Tervezés A szoftver csak a Log felületét használja Log HtmlLog TextLog Log.html Log.txt 9
Builder A TextLog és a HtmlLog képes elmenteni a naplót valamilyen fájlba A fájlok felépítése teljesen eltér egymástól A Log osztály biztosít egyszerű virtuális függvényeket az új események hozzáadásához, és azok fájlba mentéséhez Elég csupán a Log felületét használni, az izzadság szagú részleteket elrejtjük a gyerek osztályokban 10
Builder minta használata int main() { int valasz; cout << "Html vagy text?" << endl; cin >> valasz; Log * log = 0; if (valasz == 0) { log = new TextLog; if (valasz == 1) { log = new HtmlLog; log->addnewerror("a file nem letezik!"); log->addnewevent("letrejott az uj dokumentum"); log->savetofile("2013_05_12.log"); delete log; 11
Tervezés Szükség lesz egy osztályra, amely képes megjeleníteni a kezelőfelületen a naplót Különböző formátumoknál különböző kezelőfelületre van szükség Írjunk egy LogViewer osztályt, amelyből származtassuk a HtmlLogViewer és a TextLogViewer osztályokat Szintén alkalmazzunk virtuális függvényeket 12
Tervezés Log HtmlLog TextLog LogViewer HtmlLogViewer TextLogViewer 13
Tervezés: Az osztályok használata int main() { int valasz; cout << "Html vagy text?" << endl; cin >> valasz; Log * log = 0; LogViewer * viewer = 0; if (valasz == 0) { log = new TextLog; viewer = new TextLogViewer; if (valasz == 1) { log = new HtmlLog; viewer = new HtmlLogViewer; delete log; delete viewer; Ez miért veszélyes? 14
Tervezés: Az osztályok használata Több formátumnál több kódot kell beírni a különféle objektumok létrehozására Előfordulhat, hogy hibázunk, és véletlenül egy TextLogViewer-t hozunk létre egy HtmlLog mellé Bízzuk a különböző típusú objektumok létrehozását speciális osztályokra 15
Tervezés: Abstract factory Írjunk egy LogFactory osztályt, amelynek van egy createlog, és egy createlogviewer függvénye Ezek a függvények legyenek tisztán virtuálisak A createlog egy Log *-al tér vissza A createlogviewer egy LogViewer *-el tér vissza Származtassunk ebből egy TextLogFactory és egy HtmlLogFactory osztályt 16
Tervezés: Abstract factory A TextLogFactory-ban implementáljuk a létrehozó függvényeket: A createlog egy Log *-al tér vissza, amely egy dinamikusan létrehozott TextLog objektumra mutat A createlogviewer egy LogViewer *-el tér vissza, amely egy dinamikusan létrehozott TextLogViewer objektumra mutat 17
Tervezés: Abstract factory A HtmlLogFactory-ban implementáljuk a létrehozó függvényeket: A createlog egy Log *-al tér vissza, amely egy dinamikusan létrehozott HtmlLog objektumra mutat A createlogviewer egy LogViewer *-el tér vissza, amely egy dinamikusan létrehozott HtmlLogViewer objektumra mutat 18
Abstract factory (elvont gyár) class LogFactory { public: ; virtual Log * createlog() const = 0; virtual LogViewer * createlogviewer() const = 0; 19
Abstract factory (elvont gyár) class TextLogFactory: public LogFactory { public: Log * createlog() const { return new TextLog; LogViewer * createlogviewer() const { return new TextLogViewer; ; 20
Abstract factory (elvont gyár) class HtmlLogFactory: public LogFactory { public: Log * createlog() const { return new HtmlLog; LogViewer * createlogviewer() const { return new HtmlLogViewer; ; 21
Abstract factory (elvont gyár) [ ] LogFactory * factory = 0; if (valasz == 0) { factory = new TextLogFactory; if (valasz == 1) { factory = new HtmlLogFactory; Log * log = factory->createlog(); LogViewer * viewer = factory->createlogviewer(); 22
Abstract factory Használatával könnyű új formátumokat hozzáadni a szoftverhez Elkerülhető az a hiba, hogy egymással nem kompatibilis objektumokat hozzunk létre Általában elég belőle egy is, ezért sokszor Singleton-ként alkalmazzák 23
Feladat Egy olyan programot írunk, amely a memóriában tárolja a merevlemez könyvtárszerkezetének másolatát Legyen lehetőség a könyvtár hierarchia kirajzolására Egy könyvtár tartalmazhat: Egyszerű fájlokat Újabb könyvtárakat 24
Tervezés: Composition minta (összetétel) Írjunk egy File és egy Directory osztályt Írjunk egy Item osztályt, amely tartalmazza azt a felületet, amelyet a File-nak és a Directory-nak is tudnia kell (név beállítása, és a megjelenítés) Az Item a File és a Directory őse A Directory képes eltárolni Item * típusú változókat egy listában A Directory azonos módon tudja megjeleníteni a fáljait és alkönyvtárait 25
Composition class Item { protected: string name; void showspaces(int spaces) const { int index; for (index = 0; index < spaces; index++) { cout << " "; public: Item(const string & name = ""): name(name) { [ ] 26
; Composition [ ] virtual ~Item() { virtual void show(int level = 0) const = 0; void setname(const string & name) { this->name = name; const string & getname() const { return name; A showspaces függvényt a hierarchia megjelenítésekor fogjuk használni 27
Composition class File: public Item { public: ; File(const string & name = ""): Item(name) { void show(int level = 0) const { showspaces(level); cout << "FILE: " << name << endl; 28
Composition class Directory: public Item { public: Item ** items; unsigned int count; Directory(const string & name): Item(name) { [ ] items = 0; count = 0; 29
Composition ~Directory() { unsigned int index; for (index = 0; index < count; index++) { delete items[index]; delete [] items; void show(int level = 0) const { showspaces(level); cout << "DIRECTORY: " << name << endl; unsigned int index; for (index = 0; index < count; index++) { items[index]->show(level + 2); 30
Composition void add(item * newitem) { Item ** temp = new Item*[count + 1]; unsigned int index; for (index = 0; index < count; index++) { temp[index] = items[index]; temp[index] = newitem; count++; delete items; items = temp; ; 31
Composition Directory root("/");; Directory * home = new Directory("home"); Directory * boot = new Directory("boot"); File * kernel = new File("kernel.img"); Directory * docs = new Directory("documents"); Directory * recipes = new Directory("recipes"); home->add(docs); docs->add(recipes); boot->add(kernel); root.add(boot); root.add(home); recipes->add(new File("csokis_suti.txt")); recipes->add(new File("halaszle.txt")); root.show(); 32
Kimenet DIRECTORY: / DIRECTORY: boot FILE: kernel.img DIRECTORY: home DIRECTORY: documents DIRECTORY: recipes FILE: csokis_suti.txt FILE: halaszle.txt 33
Composition A File és a Directory mellé tetszőleges gyerek osztályokat is felvehetünk, így összetettebb hierarchiákat is könnyen felépíthetünk Még egy példa: Weboldalon összetett elemeket is leírhatunk vele, például egy táblázat adott cellája tartalmazhat képeket, szöveget, vagy akár újabb táblázatot is 34