• Nem Talált Eredményt

Díszítő – Decorator

In document Programozás technika (Pldal 105-109)

4. Programozási technológiák – Tervezési minták

4.3. Szerkezeti tervezési minták

4.3.2. Díszítő – Decorator

public override int getIQ() {

return robi.getMemory() / 1024; // 1GB memória = 1 IQ }

}

class Program {

static void Main(string[] args) {

Robot R2D2 = new Robot("R2D2", 80000);

Ember R2D2wrapper = new Robot2Ember(R2D2);

Console.WriteLine("Neve: {0}", R2D2wrapper.getNév());

Console.WriteLine("IQ-ja: {0}", R2D2wrapper.getIQ());

Console.ReadLine();

}

}

4.3.2. Díszítő – Decorator

A díszítő minta az átlátszó csomagolás klasszikus példája. Klasszikus példája a karácsonyfa. Attól, hogy a karácsonyfára felteszek egy gömböt, az még karácsonyfa marad, azaz a díszítés átlátszó. Ezt úgy érjük el, hogy az objektum összetételben szereplő mindkét osztály ugyanazon őstől származik, azaz ugyanolyan típusúak. Ez azért hasznos, mert a díszítő elemek gyakran változnak, könnyen elképzelhető, hogy új díszt kell felvenni. Ha díszítő egy külön típus lenne, akkor a karácsonyfa feldolgozó algoritmusok esetleg bonyolultak lehetnek.

A díszítő mintánál egy absztrakt ősből indulunk ki. Ennek kétfajta gyermeke van, alap osztályok, amiket díszíteni lehet és díszítő osztályok. A karácsonyfa példa esetén az alap osztályok a különböző fenyőfák. A díszítő osztályokat általában egy absztrakt díszítő osztály alá szervezzük, de ez nem kötelező.

A díszítés során az ős minden metódusát implementálni kell, úgy hogy, a becsomagolt példány metódusát meghívjuk, illetve ahol ez szükséges, ott hozzáadjuk a plusz funkcionalitást. Kétféle díszítésről beszélhetünk:

1. Amikor a meglévő metódusok felelősségkörét bővítjük. Ilyen a karácsonyfás példa.

2. Amikor új metódusokat is hozzáadunk a meglévőkhöz. Ilyen a Java adatfolyam (angolul: stream) kezelése, illetve a lenti kölcsönözhető jármű példa.

Mindkét esetben a példányosítás tipikusan így történik:

ŐsOsztály példány = new DíszítőN(…new Díszítő1( new AlapOsztály())…);

Mivel a csomagolás átlátszó, ezért akárhányszor becsomagolhatjuk a példányunkat, akár egy díszítővel kétszer is. Ez rendkívül dinamikus, könnyen bővíthető szerkezetet eredményez, amit öröklődéssel csak nagyon sok osztállyal lehetne megvalósítani.

Érdekes megfigyelni a minta UML ábráján, hogy a díszítő osztályból visszafelé mutat egy aggregáció az ős osztályra. Ez az adatbázis kezelés Alkalmazott – Főnök reláció megoldásához hasonlít, amikor az Alkalmazott tábla önmagával áll egy-több kapcsolatban, ahol a külső kulcs a főnök alkalmazott_ID értékét tartalmazza.

4.3.2.1. Példa

A díszítő mintát a következő példával mutatjuk be. Képzeljük el, hogy egy versenypályán üzemeltetünk egy autókölcsönzőt. Az autókölcsönzőben természetesen több típusú autót, többnyire versenyautót adunk kölcsönzésre. A lényeg, hogy előfordulhat az, hogy újabb autókkal bővítjük az állományt. Felkészülve erre, először egy alap autó osztályt hoznak létre, amelyben a bérelhető autók információi szerepelnek, mint gyártó neve, a modell neve, a bérlés időtartama körökben számolva és a bérlés díja. A kölcsönzőben időnként akciókkal kedveskednek az ügyfeleknek, valamint változó, hogy egy bizonyos autó kölcsönözhető-e, vagy sem.

Ezen extrák hozzáadását, a díszítő minta implementálásával tették lehetővé. Az alap autó osztályból származik az alap dekorátor osztály, mely elvégzi a becsomagolást. A konkrét díszítő osztályoknak már csak a funkciók kibővítésével kell foglalkozniuk. Amint egy autót feldíszítünk, mint kölcsönözhető, az már csak a bérlőjét várja, aki kiviszi a pályára. Az akciókat is díszítő osztályokkal valósíthatjuk meg. Látható, ha új autókkal bővül a parkunk, vagy újabb akciós ajánlatokat szeretnénk bevezetni, azt könnyedén megtehetjük, új konkrét autó és konkrét díszítő osztályok hozzáadásával.

4.3.2.2. Forráskód using System;

namespace DecoratorDesignPattern {

public abstract class VehicleBase // alap osztály, adott funkcionalitásokkal {

public abstract string Make { get; } public abstract string Model { get; } public abstract double HirePrice { get; } public abstract int HireLaps { get; } }

public class Ferrari360 : VehicleBase // egy konkrét autó {

public override string Make { get { return "Ferrari"; } } public override string Model { get { return "360"; } } public override double HirePrice { get { return 100; } } public override int HireLaps { get { return 10; } } }

public abstract class VehicleDecoratorBase : VehicleBase // a dekorátor osztály {

private VehicleBase _vehicle; // HAS-A kapcsolat, ezt csomagoljuk be public VehicleDecoratorBase(VehicleBase v) { _vehicle = v; }

public override string Make { get { return _vehicle.Make; } } public override string Model { get { return _vehicle.Model; } } public override double HirePrice { get { return _vehicle.HirePrice; } } public override int HireLaps { get {return _vehicle.HireLaps; } } }

public class SpecialOffer : VehicleDecoratorBase // konkrét dekorátor osztály {

public SpecialOffer(VehicleBase v) : base(v) { } public int Discount { get; set; }

public int ExtraLaps { get; set; } public override double HirePrice {

get {

double price = base.HirePrice;

int percentage = 100 - Discount;

return Math.Round((price * percentage) / 100, 2);

} }

public override int HireLaps { get { return (base.HireLaps + ExtraLaps); } } }

public class Hireable : VehicleDecoratorBase {

public Hireable(VehicleBase v) : base(v) { } public void Hire(string name)

{

Console.WriteLine("{0} {1} típust kölcsönzött {2} {3}$-ért {4} körre.\r\n", Make, Model, name, HirePrice, HireLaps);

} }

class Program {

static void Main(string[] args)

{

Ferrari360 car = new Ferrari360();

Console.WriteLine("Alap Ferrari360:\r\n");

Console.WriteLine("Alap ár: {0}, alap tesztkörök száma: {1}\r\n\r\n", car.HirePrice, car.HireLaps);

SpecialOffer offer = new SpecialOffer(car);

offer.Discount = 25;

offer.ExtraLaps = 2;

Console.WriteLine("Speciális ajánlat:\r\n");

Console.WriteLine("Különleges ajánlat ára: {0}, {1}$-ért\r\n\r\n", offer.HirePrice, offer.HireLaps);

Hireable hire = new Hireable(car);

hire.Hire("Bill");

Hireable hire2 = new Hireable(offer);

hire2.Hire("Jack");

Console.ReadLine();

} } }

4.3.2.3. UML-ábra

20. ábra

4.3.2.4. Gyakorló feladat

Az alábbi leírás szerint készítsünk forráskódot. A megoldáshoz használjuk a díszítő tervezési mintát!

Feladat

Nekem senki ne mondja, hogy egy programozó élete unalmas, azon kívül, hogy szabadidejében ugyanazokat a dolgokat teheti, mint más rendes ember. Még a munkájában is kaphat érdekes megbízatásokat. A minap kaptunk is: egy bringa boltot kellett csinálnunk. Mi már láttuk is a szemünk előtt a sok csillogó-villogó bringát fel alá gurulni a szalonban. Okosan vagy inkább objektumorientáltan-t kéne mondanom, készítettünk egy absztrakt osztályt Bringa_Alap néven, majd ebből az osztályból származtattuk a Bringa21seb, Bringa_Csengővel, Bringa_Női, stb. osztályokat, ezek az osztályok tudták a konkrét példány árát. Telt múlt az idő és a vásárlók

igényeit követve már ilyen osztályneveket használtunk:

BringaCsengovel21sebAluvazSarvedoveldecsakElolAkcios. Szép ugye? Éreztük rögtön, hogy ez így nem lesz jó, arról nem is beszélve, hogy osztályaink számának növekedése hasonlított egy demográfiai robbanásra.

Hosszas tanácskozás után kénytelenek voltunk belátni, hogy majdnem az egész kódot ki kell dobni, bár az első, absztrakt Bringa_Alap osztályt megtartottuk. Ebben írtunk egy getLeírás és egy Ár nevű absztrakt függvényt a hozzájuk tartozó mezőkkel. Ebből öröklődött a konkrét Bringa, de még kellettek az alkatrészek, csengő, váltó, sárvédő, stb. Így létrehoztunk egy újabb absztrakt osztályt, amit Bringa_Díszítőnek neveztünk el és ez is a Bringa_Alap gyermek. A Díszítőből származnak a konkrét elemek, amelyek csak a saját áraikat ismerik, de az Ár függvényük és a konstruktoruk úgy van megírva, hogy az őket hívó elem árát is hozzáadják az árhoz.

Tulajdonképpen veszünk egy bringát, majd „körbecsomagoljuk” (ezért nevezik ezt a mintát wrapper-nek is) egy sárvédővel, majd ezt egy csengővel, és így tovább. Amikor minden igényt kielégítettünk meghívjuk a legutolsó elem Ár függvényét, mely a saját árával meghívja a következő elem ugyanezen függvényét, és a végén visszakapjuk az összeállítás teljes árát.

Feladat

Készítsen egy kávé ital programot, amely szemlélteti a díszítő működését! A feladat szempontjából csak az ár és a kávé összetevői számítsanak (pl. cukor, tejszín, tej, hab, esetleg rum, öntet)! A program vegye figyelembe az árak alakulását is. A feladat az, hogy a kezdetben üres, keserű, fekete kávénkat díszítsük fel.

In document Programozás technika (Pldal 105-109)