Miskolci Egyetem, Általános Informatikai Tanszék
Programtervezési minták (GEIAL517M) Jegyzőkönyv
Tartalom 1
2
3
Létrehozó minták................................................................................3 1.1
Egyke...........................................................................................3
1.2
Factory.........................................................................................6
1.3
Builder.........................................................................................7
1.4
Prototype....................................................................................9
Szerkezeti minták.............................................................................10 2.1
Adapter......................................................................................10
2.2
Bridge........................................................................................12
2.3
Composite (Össztétel).............................................................15
2.4
Decorator (Díszítő)..................................................................17
2.5
Proxy..........................................................................................19
2.6
Facade (Homlokzat).................................................................21
Viselkedési minták...........................................................................23 3.1
Command (Parancs)................................................................23
3.2
Observer (Megfigyelő)............................................................26
3.3
Chain of Responsibility (Felelősség lánc)............................29
3.4
Mediator (Közvetítő)...............................................................31
3.5
State (Állapot)..........................................................................35
3.6
Strategy (Stratégia)................................................................38
2
1 Létrehozó minták 1.1Egyke Feladat: •
A fogyasztó saját ritmusában, saját algoritmusa szerint működve felhasználja a termelő által előállított terméket.
•
Legyen a raktár egy csak egy példányban létező objektum. o a termelő beteszi a terméket, o a fogyasztó pedig kiveszi
•
A fogyasztó és termelő számra nem kötött legyen véletlen érték 1-100 között (az elején érdemes fix darabszámmal dolgozni)
•
A termelő és a fogyasztó sebességére nincs kikötésünk, így minden szálban véletlen ideig várakozzunk (100-1000 ms)
•
A raktár limitált méretű, csak 3000 terméket képes tárolni.
•
A termék fontos tulajdonsága a neve, amely azonosítja a létrehozó szál neve + sorszáma.
•
Készítsen osztály diagramot az alkalmazás modellezésére!
A feladat osztálydiagramja:
3
A diagram részei: Producer: Kliens oszály, ismeri a terméket és a raktárt Consumer: Kliens oszály, ismeri a terméket és a raktárt Repository: A Singleton. Definiál egy Instance statikus property-t, mellyel elérhetővé teszi az egyetlen példányt. A statikus get property lockolva van a párhuzamos hozzáférés miatt public static Repository Instance { get { lock (Repository.lockString) { if (instance == null) { instance = new Repository(); } return instance; } } } A termelő és a fogyasztó a raktár példányát csak a statikus property-n keresztül éri el. Ha már volt példányosítva akkor visszatér a példánnyal, ha még nem, akkor létrehozza az új raktár példányt. 4
Product: A termék osztálya A Singleton repository használata egyszerű: •
Új termék hozzáadása: Product p = Repository.Instance.AddProduct(new Product());
•
Termékeket így vehetünk ki a raktárból: Product p = Repository.Instance.GetProduct();
A Singleton kiváltja a globális változókat, így szabályozottan férhetünk hozzá globális objektumhoz.
5
1.2Factory Feladat: Az előző (termelő-fogyasztó) feladatban Különböző megvalósítású, de azonos felületű termékek létrehozása Factory mintával, hogy a termelőktől és a fogyasztóktól elszigeteljük a konkrét típusokat. Cél: Új termék bevezetésekor a termelőnek és a fogyasztónak minimális módosítás, ismeret legyen szükséges. A feladat osztálydiagramja
Részei: •
Az előző feladatból: Consumer, Repository, Producter. A szerepük nem változott.
•
Producer: A producer az előző feladatban betöltött szerepen kívül, ő lett a kliens. Ismeri a terméket és a gyárat, terméket kér a gyártól.
•
Product: Fejlődött az előző feladathoz képest. A Product absztrakt osztály, a termékek jellemzőit foglalja össze.
6
•
Product1, Product2: Konkrét termékek. Közös ősük a Product ostály.
•
ProductFactory: Van egy create metódusa, mely Product típusú objektummal tér vissza. Adott paraméter alapján eldönti, hogy milyen konkrét productot hozzon létre.
Példa termékek példányosítására: ProductFactory productFactory = new ProductFactory(); Random r = new Random(); Product p = null; p = productFactory.Create(r.Next(1, 3));
A minta használatával új termékek bevezetésekor a kliens csak a paraméter változását tudja, változtatni csak a ProductFactory létrehozó metódusát kell.
1.3Builder Feladat: Készítsen egy C# alkalmazást, amely komponensek létrehozásának menetét mutatja be! • •
•
Készítse el a komponens osztályt! o Legyen ToString metódusa, amely kiírja nevét, és az aktuális szerkezetét! A komponensek különböző felépítésük lehetnek (van neve string): o Portok 0..* (van neve, id-je, amely legyen egyedi, kapacitása) adat (kimenet vagy bemenet) típus (string) o LogInterfész 0|1 (van id-je) Készítsen építő osztályokat tipikus komponensekre! o Adatszűrő (1 bementi adatportja és 1 kimeneti adatportja van) o Monitor (1 bementi adatportja és 1 kimeneti adatportja van) o Információ forrás (1 kimeneti adatportja van) o Információ nyelő (1 bementi adatportja portja van)
7
A feladat osztálydiagramja
•
A ComponentBuilder absztrakt osztály ismeri a Component osztály részeit és tartalmazza a Component osztály részeit előállító metódusokat.
•
A Director osztály ismeri a ComponentBuilder absztrakt osztályt és használja metódusait.
•
A Port és a LogInterface osztályok a Component részei.
•
A klines létrehozza a Director-t és átadja neki ComponentBuilder megfelelő leszármazott osztályának a példányát.
A főprogram: Director director = new Director(); ComponentBuilder cb = new MonitorBuilder(); director.SetComponentBuilder(cb); director.Create(); Component c = director.GetComponent(); cb = new DataFilterBuilder(); director.SetComponentBuilder(cb); director.Create(); Component c2 = director.GetComponent(); Console.WriteLine(c.ToString()); Console.WriteLine(c2.ToString()); 8
A program kimenete:
A minta használatával a termék példányosítása ellenőrzötten történik, csak a director felügyelete mellett lehetséges, ami csak kész terméket ad át a kliensnek.
1.4Prototype Az előző feladat bővítése: •
A felhasználó tudjon több komponens példányt is létrehozni, amelyet egy listában tárolunk!
•
Az új példányok mindig egy alapértelmezett értékkel jönnek létre, amelyet a felhasználó megváltoztathat.
•
Gondoskodjon arról, hogy a listában levő elemek állapotát meg lehessen változtatni (konzol alkalmazásban egy menü)!
•
Gondoskodjon arról, hogy a klónozott objektumok teljesen függetlenek legyenek a prototípus objektumtól (deep copy)
A kibővített osztálydiagram:
9
A Prototype interfésznek van egy Clone metódusa, mellyel önmagát klónozza. A Comonent osztály implementálja a Prototype interfészt, ez az osztály a konkrét prototípus a modellben. A Prototípus minta egy egyszerű és hatékony módja objektumok másolásának. Az objektumok inicializálásának idejét rövidíti.
2 Szerkezeti minták 2.1Adapter Az adapter minta használatának gyakorlásához egy egyszerű példa elkészítése a feladat, amelyben szándékosan más néven akarjuk használni a metódusokat. Az elkészített feladatban egy egyszerű, négy alapműveletes számológép osztályhoz csinálok egy példát az osztály adaptálására.
10
A diagram részei: •
ICalculator: A cél felület interfésze. A Client osztály ismeri.
•
Client: Az ügyfél osztály, amely felhasználja a Target interface objektumát
•
RealCalc: Az illesztendő felület, meghatározza az interfészt, melyet illeszteni kell (az adaptee).
•
MyCalculator: Összeilleszti az ICalculator interfészt a RealCalc felületével
Példa a használatára: ICalculator calc = new MyCalculator(); double a = 20; double b = 5; Console.WriteLine("{0} Console.WriteLine("{0} Console.WriteLine("{0} Console.WriteLine("{0}
+ * /
{1} {1} {1} {1}
= = = =
{2}", {2}", {2}", {2}",
11
a, a, a, a,
b, b, b, b,
calc.Add(a, b)); calc.Subtract(a, b)); calc.Multiply(a, b)); calc.Divide(a, b));
A kliensnek csak az ICalculator felületét kell ismernie, az adapter elrejti a művelteket ténylegesen végrehajtó osztályok részleteit.
2.2Bridge Feladat: Készítsen egy grafikus alkalmazást, amely •
3 féle objektumot kezel: kör, négyzet, kereszt
•
Mindegyiknek legyen alapértelmezett mérete: o A körnek az átmérő o A négyzetnek a két átfogó csúcs távolsága o A keresztnek a hossza
•
Helyezzen el véletlenszerűen grafikus objektumokat a képernyőn
•
Tegye lehetővé, hogy bármelyiket egy közös dialógusablakon (vagy frame) keresztül lehessen átméretezni, áthelyezni! (Vagy klikkelés, vagy combobox a kiválasztásra)
•
Ha valaki megváltoztatja a kijelölt objektumot, akkor a beállító helyen az aktuális értéket lássuk!
•
Használja a Bridge mintát!
•
Definiáljon egy Shape osztályt, aminek van egy implementora, ami kör vagy négyzet vagy plusz jel.
•
A Shape-nek vannak position, size tulajdonságai
•
Ezek a tulajdonságok az implementort használják arra, hogy megváltoztassák az objektumok paramétereit 12
•
Érdemes a kirajzoláshoz is a Shape osztályt használni.
Kép az elkészített alkalmazásról:
A feladat osztálydiagramja
Részei
13
•
Shape: Az elvont ábrázolás felület. Felületet biztosít a Circle, Square és Cross osztályoknak. A hozzá érkező kéréseket az IShapeDrawer Implementor felé továbbítja.
•
Circle, Square, Cross: A finomított elvont ábrázolás osztályai.
•
IShapeDrawer: Az implementor (megvalósító felület) osztály. Kirajzoló felületet biztosít.
•
CanvasDrawer: A konkrét megvalósító osztály. Implementálja a az IShapeDrawer Implementort a wpf-es Canvas controlra tudja kirajzolni az alakzatokat és tuja törölni a Canvas-t.
A grafikus objektumok létrehozása: Random r = new Random(); cd = new CanvasDrawer(canvas1); for (int i = 0; i < 30; i++) { int num = r.Next(1, 4); Shape s = null; switch (num) { case 1: s = new Cirlcle(this.cd); break; case 2: s = new Square(this.cd); break; case 3: s = new Cross(this.cd); break; } s.Size = r.Next(10, 50); s.Y = r.Next(200); s.X = r.Next(500); s.Id = i; Shapes.Add(s); } Ha változik valamilyen paraméter, akkor az alakzatok újra rajzolása a következőképpen történik: cd.Clear(); 14
foreach (Shape s in Shapes) { s.Draw(); } A minta használatával az objektumokat kirajzoló programrészt lecserélhetjük úgy, hogy a többi osztályt nem kell módosítani (ha például a WPF-es Canvas helyett valami mást akarnék használni).
2.3Composite (Össztétel) Feladat: •
A komponensek névterekbe vannak hierarchikusan rendezve. Egy névtérben lehetnek komponensek és újabb névterek is.
•
Minden komponensnek vannak tulajdonságai és metódusai a. Legfontosabb a komplexitás, ami egy lebegőpontos szám (mennyire bonyolult a komponens feladata) b. ToString metódus: összesítés, melyik elemből mennyi, majd alelemeit alatta kiírja c. ToXML metódus: a ToString XML verziója d. Count tulajdonság: az alatta lévő összes elem (komponens és névtér) e. ComponentCount tulajdonság: az alatta lévő komponensek száma. f. ContextCount tulajdonság: az alatta lévő komponensek száma
•
Készítsünk egy alkalmazást, amely ezt a fa struktúrát kompozit minta alkalmazásával valósítja meg! a. Adott részfa (csomópont) elemére lekérdezhetjük, hogy mennyi a komplexitása (áthelyezhető-e a részfa egésze egy processzorra)! b. Készítsük el a ToString, ToXML metódusokat!
•
Készítsünk egy grafikus alkalmazást, amely a fenti elemeket használja, és egy fa vezérlőben mutatja az elemeket!
Kép az elkészült alkalmazásról:
15
A feladat osztálydiagramja
Component: A faszerkezet levele (egy komponens) SoftwareElement: Az összetétel osztályok (Component és NameSpace) közös felülete. NameSpace: Összetétel osztály. Lehetnek gyerekei: NameSpace, vagy Component. 16
Részlet a hierarchia létrehozásáról: SoftwareElement root = new NameSpace("RootNS"); SoftwareElement c1 = new Component("component1", 2); SoftwareElement c2 = new Component("component2", 4); ... (root as NameSpace).Add(c1); (root as NameSpace).Add(c2);
Így olyan osztályhierarchia valósul meg, amely az alap objektumok rekurzívan építi egyre bonyolultabb objektumokká A kliensnek nem kell tudnia, hogy a kérést egy komponensnek vagy névtérnek adja át, ugyanis ezek egységesen vannak kezelve, ha a kérést levél kapja, akkor az végrehajtódik, ha belső csomópont, akkor továbbítódik a levelek felé.
2.4Decorator (Díszítő) Példafeladat: •
Készítsen egy Component osztályt, amely képes végrehajtani egy operációt, majd különböző dekorátorokkal módosítsa a viselkedését.
•
Készítsen egy dekorátor osztályt, amely naplózza a mozgás tényét.
•
Készítsen egy dekorátor osztályt, amely kimondja a kapott parancsot, és ha a pozícióba ér (esemény).
•
Készítsen egy objektumot, amely naplózza is a mozgás tényét és ki is mondja a kapott parancsot.
Az elkészített objektum: Component c; RobotComponent rc = new RobotComponent(new Point() { X = 0, Y = 0 }); MovingLoggerDecorator mld = new MovingLoggerDecorator(rc); SpeakerDecorator sd = new SpeakerDecorator(mld); c = sd;
17
c.MoveTo(new Point() { X = 12, Y = 54 }); A két dekorátor hozzáadásával a robot komponens megkapta a további képességeket. Az elkészített konzolos alkalmazás kimenete:
Az elkészített feladat osztálydiagramja:
18
Az osztálydiagram részei •
Component: Absztrakt osztály, megadja a felületet azoknak az objektumoknak, melyek dinamikusan bővíthetőek lesznek. Egy absztrakt metódusa van.
•
RobotComponent: A Component osztályból származik, implementálja a MoveTo metódust.
•
ComponentDecorator: Egy Component típusú adattagja van. A Component típusú objektumokat tudja bővíteni, meghatároz egy felületet, amely illeszkedik a Component-re.
•
SpeakerDecorator: A Component MoveTo metódusát kiegészíti úgy, hogy az naplózza a mozgás tényét.
•
MovingLoggerDecorator: A Component MoveTo metódusát kiegészíti úgy, hogy kimondja a kapott parancsot, és ha a pozícióba ér.
A minta használatának előnye, hogy a díszítők láncolásával több tulajdonságot egyszerűen tudnunk hozzáadni futásidőben.
2.5Proxy Feladat szövege •
Készítsen konzolalkalmazást, ami web service-t használva végez el valamilyen műveletet.
•
A szolgáltatás elérése proxy objektumon keresztül történjen. A kliens kódon ne látszódjon, hogy a művelet egy távoli gépen hajtódik végre.
Feladat megoldása: 1. Létrehoztam egy új ASP.NET webes alkalmazást és egy Console alkalmazást 19
2. A webalkalmazásban létrehoztam egy WebCalc nevű webservice-t, ami tud összeadni, kivonni, szorozni és osztani. 3. A webalkalmazást elindítottam a localhoston, a service wsdl elérhető volt a következő címen: http://localhost:11414/Services/WebCalc.asmx?WSDL 4. Ez után létrehoztam a console alkalmazást és Visual Studioban hozzáadtam a projekthez egy web referenciát az előbb létrehozott webservice-re. 5. A console alkalmazás Main metódusában használtam a service-t a Visual Studio által generált proxy osztállyal.
static void Main(string[] args) { WebCalc.WebCalcSoapClient webCalcProxy = new WebCalc.WebCalcSoapClient(); int a = 3; int b = 6; Console.WriteLine("{0} + {1} = {2}", a, b, webCalcProxy.Add(a, b)); Console.ReadKey(); } Az így létrehozott WebCalcSoapClient osztály példánya miatt a távoli végrehajtás helyinek „tűnik”. A Visual Studio által generált WebCalcSoapClient valójában soap kérés küld a webes szolgáltatásnak. A Console alkalmazás kimenete:
20
2.6Facade (Homlokzat) Feladat szövege: Készítsen konzolos alkalmazást, ami egy számítógép elindulását modellezi. •
A számítógép legalább 4 komponensből álljon.
•
Használja a facade mintát.
A feladat osztálydiagramja:
21
A diagram részei: •
Computer: A homlokzat. A kliens (user) kéréseit továbbítja az alrendszerek osztályai felé.
•
Alrendszeri osztályok: CPU, Keyboard, VideoCard, DDRRAM, HDD. Az alrendszeri osztályok nem tudják, hogy ők egy Computer részei, csak elvégzik a kéréseket.
A feladatban a Computer osztály Start metódusa modellezi egy számítógép elindulását. Beállítja a billentyűzet kapcsolóit, majd megnézi, hogy nyomtak-e billentyűt. Ha igen, akkor elindítja a BIOS-t, egyébként megkezdi a bootfolyamatot. Betölti a memóriába winchester bootszektorát, majd beállítja a processzor program counter regiszterét a memória boot címére. this.keyboard.CapsLock = false; this.keyboard.NumLock = false; this.keyboard.ScrollLock = false; if (this.keyboard.Puffer.Count != 0) { this.BiosStart(); } this.vcard.Display("Start computer...\n"); this.memory.Load(RAM.BootAddress, this.hdd.Read(HDD.BootSectorAddress, HDD.SectorSize)); this.cpu.Jump(RAM.BootAddress); this.cpu.Execute(); A használatának fő előnye, hogy a nagyon bonyolult metódushívásokat összegyűjthetjük egy metódusba. Az alrendszerek között jelentősen csökkentik az egymástól függést, laza kapcsolat alakítható ki közöttük.
22
3 Viselkedési minták 3.1Command (Parancs) Készítsen egy grafikus számológép alkalmazást, amely •
Operatorokat, operandusokat (számokat) tartalmaz,
•
Látható a történet,
•
Lehetséges tetszőleges számú művelet visszavonása (Undo),
•
Lehetséges a többszöri ismétlésre (Redo),
•
Véglegesítés (Commit), ilyenkor a történet kezdőpontja az adott pont lesz, előtte levő műveletek nem láthatóak nem vonhatóak vissza,
•
A történetből tetszőleges elem újra elvégezhető.
A command pattern-t implementáló számológép alkalmazást WPF-ben írtam meg. Képek az alkalmazásról:
23
24
A feladat megoldásának osztálydiagramja
Részei: •
CalculatorCommand: absztrakt osztály. parancsaihoz biztosít egy absztrakt ősosztályt
•
A CalculatorCommand leszármazottai egymáshoz rendelik a Calculator-t (a fogadó osztályt) és egy-egy műveletet. A Calculator megfelelő metódusainak hívásával implementálják az Execute és az Unexecute metódusokat
A
számológép
o AddCommand: Parancs osztály az összeadáshoz. o SubtractCommand: Parancs osztály a kivonáshoz. o MultiplyCommand: Parancs osztály a szorzáshoz. o DivideCommand: Parancs osztály az osztáshoz. •
MainWindow: Hívó és ügyfél a modellben. Létrehozza a CalaculatorCommand példányokat és beállítja a fogadó osztályt, felkéri a létrehozott CalculatorCommandot, hogy hajtasa végre (Execute) a műveletet, vagy vonja vissza (UnExecute) a legutóbb végrehajtott műveleteket. Mindig a legutóbb végrehajtott, még nem visszavont műveletet vonja vissza.
•
Calculator: A implementálja.
fogadó
osztály.
25
A
számológép
műveleteit
Példa a használatra: Parancsokat hozunk létre végrehajtjuk és tároljuk. calculator = new Calculator(); switch (lastOperation) { case "+": calculatorCommand = new AddCommand(calculator, value); break; case "-": calculatorCommand = new SubtractCommand(calculator, value); break; case "/": calculatorCommand = new DivideCommand(calculator, value); break; case "*": calculatorCommand = new MultiplyCommand(calculator, value); break; default: throw new InvalidOperationException("Operation: " + lastOperation); } calculatorCommand.Execute(); Commands.Add(calculatorCommand); Így a kérelmeket objektumokba ágyazzuk, a kliensnek különböző kéréseket adhatunk át, amelyeket sorba rendezhetünk és vissza is vonhatunk. Mivel objektumba vannak ágyazva tudjuk a kéréseket ideiglenesen tárolni, könnyíti a kérések naplózását.
3.2Observer (Megfigyelő) Feladat: Készítsen grafikus alkalmazást, amely -
Lehetőséget biztosít egy szín érték beállítására,
-
Felső részén 3 db csuszka található (R,G,B) színkomponensek állítására,
-
Alsó részeiben a beállított érték megjelenítése található: 26
o 3 db csak olvasható beviteli mező, o Egy adott színű panel, o 3 oszlop megfelelő magassággal. -
Ha változtatjuk a felső részben levő csuszkát, akkor az alsó mezőben levő nézetek frissüljenek automatikusan.
Képek az elkészített alkalmazásról:
27
A feladat osztálydiagramja
Részei: IColorPickerSubject: Az alany interfész ebben a modellben. Interfész a megfigyelők csatolásához és értesítés küldéshez. IColorPickerObserver: Frissítő interfész az értesítendő objektumoknak. SliderValues: Az alany interfészt implementálja. Három értéket tartalmaz, a csuszkákhoz. A csuszkák változásáról értesítést küld a megfigyelőknek. RGBColor: Megfigyelő interfészt implementáló osztály. Egy színt reprezentál az RGB színkomponensekkel és ecset objektumot, amely színe az adott RGB. Columns: Megfigyelő interfészt implementáló osztály. A három tulajdonsága a három oszlop magassága százalékban. A minta használatával az objektumok létrehozása a következőképpen történik: public class ColorPickerDataContext { private RGBColor rgbColor; private SliderValues sv; private Columns columnHeights; public ColorPickerDataContext() 28
{ this.rgbColor = new RGBColor(); this.sv = new SliderValues(); this.columnHeights = new Columns(); sv.RegisterObserver(rgbColor); sv.RegisterObserver(columnHeights); } ... Innentől, az sv változások esetén értesíti a megfigyelőit. Az alany és a megfigyelők között laza kapcsolat van. Az alany tud a megfigyelőkről, de egyéb információt nem tud, nincs is szükség rá, hogy tudjon. További előnye, hogy könnyen adható hozzá új megfigyelő, a meglévő kód módosítása nélkül.
3.3Chain of Responsibility (Felelősség lánc) Feladat: •
Készítsünk egy dialógus alapú grafikus alkalmazást, melyben három gomb (Betöltés, Mentés, Kilépés) található, és két check boksz (automatikus, manuális)!
•
Attól függően, hogy melyik elem van kiválasztva, jelenítsünk meg help információt!
•
A kilépés gomb kivételével minden elemhez legyen segítség információ, a dialógus ablakhoz is!
•
Alakítsuk ki a következő felelősség láncot: Betöltés -> Mentés -> Összes gomb -> Alkalmazás
•
Az általános Handle osztályba kell egy SetNextHandle metódus!
Kép az elkészített alkalmazásról:
29
A feladat osztálydiagramja: •
HelpHandler: A help rendszer kéréseinek kezeléséhez definiál egy interészt. Implementálja a következő egyedhez a kapcsolatot.
•
SaveButtonHandler, LoadButtonHandler, ButtonHelpHandler, FormHelphandler: Lekezeli a megfelelő kéréseket. Hozzáfér az utána következő help handlerhez. Ha tudja kezelni a kérést, akkor kezeli, egyébként továbbítja a következő help handlernek.
•
Client: Létrehozza a láncot, kéréseket indít a lánc elején lévő help handlernek.
Az elkészített alkalmazásban a felelősség lánc a következőképpen épül fel: ... private HelpHandler startHelpHandler; 30
public HelpSystem() { InitializeComponent(); SaveButtonHelpHandler SaveButtonHelpHandler(); LoadButtonHelpHandler LoadButtonHelpHandler(); ButtonHelpHandler bhh FormHelpHandler fhh =
sbhh = new lbhh = new = new ButtonHelpHandler(); new FormHelpHandler();
sbhh.SetNextHandler(lbhh); lbhh.SetNextHandler(bhh); bhh.SetNextHandler(fhh); this.startHelpHandler = sbhh; } ... A lánc kialakítása után help üzenetet a következő metódus állítja be: private void sethelpMessage(object sender) { string name = ((Control)sender).Name; Type type = sender.GetType(); toolStripStatusLabel2.Text = this.startHelpHandler.Handle(name, type); }
A minta használatával egyszerűsödik a help információk megjelenítése. Új GUI elem bevezetésével csak egy új osztályt kell elkészíteni, amely a HelpHandlerből származik. A kód többi részét nem kell módosítani.
3.4Mediator (Közvetítő) Közvetítő (Mediátor) minta Feladat szövege: •
Készítsünk egy dialógus alapú grafikus alkalmazást, melyben o egy városlista lista ablak található a lehetséges elemekkel (városok nevei) 31
o beviteli mező, o másik kiválasztott városok lista a felvett városok neveivel, o töröl gomb •
A következőképpen működjön: o Ha a városlista egy eleme ki van választva, akkor
másoljuk be a szöveg tartalmát a beviteli mezőbe,
engedélyezzük a hozzáad gombot
o Az átmásolt szöveget a felhasználó át tudja írni. o Ha a hozzáad gombot megnyomtuk, akkor
a szöveges mező tartalmát hozzáadja a kiválasztott városok listájába
a töröl gomb engedélyezett lesz
o Ha a töröl gombot megnyomtuk, akkor
a kiválasztott városok lista törlődik,
a töröl gomb tiltódik,
a hozzáad gomb tiltódik
Képek az elkészített alkalmazásról:
32
A feladat osztálydiagramja:
33
Az osztálydiagram részei •
CityChooserMediator: A közvetítő felület a kollégák között
•
Mediator: Konkrét közvetítő. Ismeri a feladatban szereplő objektumokat. A Copy metódussal a SelectedCity Name property-jét állítja be. Az Add hozzáad egy nevet a városok listájához. A Choose hozzáad egy nevet a kiválasztott városok listájához.
•
CityChooserColleague: Felület a kollégáknak. Ismeri a CityChooserMediatort.
•
City: Konkrét kolléga osztály. Városlistát tartalmaz. A Choose metódussal jelzi a mediátornak, hogy kiválasztottuk egy elemét. Az Add metódussal hozzáad egy nevet a CityList propertyjéhez. A Clear metódus törli az elemeit.
•
SelectedCity: Konkrét kolléga osztály. Egy nevet tartalmaz. A Choose metódussal jelzi a mediátornak, hogy property-jét hozzá kell adni a kiválsztott városokhoz.
A minta használata: Példányosítjuk az osztályokat a következő módon: SelectedCity selected; City cities; City choosenCities; Mediator m; 34
m = new Mediator(); cities = new City(m); choosenCities = new City(m); selected = new SelectedCity(m); m.Cities = cities; m.ChoosenCities = choosenCities; m.Selected = selected; A kollégák ismerik a mediátort, de egymást nem és a mediátor is ismeri a kollégákat. Ez a minta laza kapcsolatot eredményez az objektumok között. Központosított vezérlést eredményez.
3.5State (Állapot) Feladat: Média lejátszó modellezése specifikáció alapján az állapot minta felhasználásával. Műveletek: pillanat állj, stop, előre lejátszás, hátrafele lejátszás, sebesség felező, sebesség kétszerező, elejére teker, végére teker. •
Alapból a film a 0 pozíción áll, és nem megy a lejátszás (stop állapot).
•
Szimuláljunk egy 30 frame/sec-es videót, ami 5 perces, progressbar segítségével!
•
Írjuk ki az alkalmazás állapotát! Írjuk ki az aktuális frame értékét, frissítsük a progressbart 100 ms-onként!
•
1. ábra A Média lejátszó
Frissítsük a vezérlők értékét az állapottól függően!
Működés Állapo t\funk ció
Pause
Forewar d
Backward
Pillan at állj
Nem érhető el, nem csinál semmit
Megállítja a lejátszást , állapotvál tás
Megállítja a lejátszást, állapotváltá s
35
Begin
Nem érhető el, nem csinál semmit
End
Nem érhető el, nem csinál semmit
Stop
Nem érhető el, nem csinál semmit
Megállítja a lejátszást , és a 0 pozícióra állít, állapotvál tás
Megállítja a lejátszást, és a 0 pozícióra állít, állapotváltá s
Nem érhető el, nem csinál semmit
Nem érhető el, nem csinál semmit
Előre játszá s
Elkezd lejátszani előre 1x sebességg el, állapotvált ás
Ha más sebesség gel játszik, akkor visszaállít 1x előre játszásra
Nem érhető el, nem csinál semmit
Elkezd lejátszani előre 1x sebességgel, állapotváltás
Nem érhető el, nem csinál semmit
Hátra játszá s
Elkezd lejátszani hátrafele 1x sebességg el, állapotvált ás
Nem érhető el, nem csinál semmit
Ha más sebességge l játszik, akkor visszaállít 1x hátra játszásra
Nem érhető el, nem csinál semmit
Elkezd lejátszani hátrafele 1x sebességgel, állapotváltás
Sebes ség felező
Nem érhető el, nem csinál semmit
A sebesség et felezi
A sebességet felezi
Nem érhető el, nem csinál semmit
Nem érhető el, nem csinál semmit
Sebes ség kétsz erező
Nem érhető el, nem csinál semmit
A sebesség et kétszerez i
A sebességet kétszerezi
Nem érhető el, nem csinál semmit
Nem érhető el, nem csinál semmit
Elejér e ugrik
Pozíció a végére
Pozíció az elejére, és játszik tovább
Pozíció az elejére, és stop állapot
Nem érhető el, nem csinál semmit
Pozíció az elejére, és nem játszik tovább
Végér e ugrik
Pozíció az elejére
Pozíció a végére, és stop állapot
Pozíció a végére, és játszik tovább
Pozíció a végére, és nem játszik tovább
Nem érhető el, nem csinál semmit
Kép az elkészített alkalmazásról:
36
A feladat osztálydiagramja:
Részei: •
MediaPlayer: A állapot környezet osztály.
•
PlayingState: Az MediaPlayer állapot kontextus állapotainak felülete
•
Begin, End, Stop, Pause, PlayingForward, PlayingBackward: Kokrét állapotai a MediaPlayer kontextusnak.
A MediaPlayer osztály példányosítása (a Video konstruktorának megadjuk a frame-ek számát és a rátát): ... MediaPlayer mp; ... 37
Video video = new Video(9000, 30); //five minutes this.mp = new MediaPlayer(video); this.mp.State = new Stop(); ...
A minta használatával egyszerűbb, érthetőbb kódot kapunk. Könnyebb modellezni az összetett állapotokat. Ebben a feladatban volt 8 féle funkció és 6 féle állapot ami a minta használata nélkül ami egy 48 case-t tartalmazó switch lenne a minta nélkül. Könnyebb új állapotot felvenni mint egy hosszú switch – case szerkezetet átlátni.
3.6Strategy (Stratégia) Feladat: Bővítsük az előző feladatot több média típus lejátszására •
Mp3
•
Wmv
•
Avi
Az aktuális média típust is kiírattam az alkalmazásban A futást ábrázoló képen lévő lejátszó így indult
A feladat kibővített osztálydiagramja:
38
A diagram részei •
Media: A wmv, avi és mp3 algoritmuscsaládokhoz ad egy felületet.
•
WMV, AVI, MP3: Konkrét algoritmuscsaládok. Az adott formátumnak megfelelően implementálják a médiát.
•
MediaPlayer: a stratégia környezet osztály a wmv, avi és mp3 algoritmuscsaládokhoz.
Az alkalmazásban egy wmv lejátszása a következőképpen történik: ... MediaPlayer mp; ... Media media = new Wmv(9000, 30); this.mp = new MediaPlayer(media); this.mp.State = new Stop(); ... A stratégia programtervezési minta használata egyszerűbb, érthetőbb kódot eredményez, mintha ugyanezt a működés a minta használata nélkül érnénk el, mivel a minta elrejti az algoritmus specifikus adatokat. Új algoritmus családdal bővíthetjük a mintát anélkül, hogy bármelyik osztályt módosítani kellene. Az aktuális algoritmus család futásidőben módosítható.
39