Download presentation
Presentation is loading. Please wait.
1
GoF projektavimo šablonai
Dviejų paskaitų tikslas – supažindinti su keletu GoF projektavimo šablonų ir jų taikymu POST
2
POST klasių diagrama su GoF projektavimo šablonais
3
Pirkimo proceso modeliavimas
4
Antra projekto iteracija realizuojant POST panaudos atvejus
Realizuoti sąveiką su išorinėmis tarnybomis šių išorinių tarnybų interfeisai gali kisti! Realizuoti sudėtingesnes prekių kainos apskaičiavimo taisykles. Užtikrinti galimybę pasirinkti naujas biznio taisykles sistemos funkcionavimo metu. Kokybiškame objekiškai orientuotame projekte, minėtus uždavinius galima įgyvendinti tik naudojant projektavimo šablonus. Projekte paprastai naudojama daug vienas su kitu susijusių projektavimo šablonų.
5
Adapter Problema: kaip organizuoti vieningą veiklą esant nesuderinamiems interfeisams, t.y. kaip adaptuoti skirtingus interfeisus? Sprendimas: konvertuoti pradinį komponento interfeisą į kitą pavidalą tarpinio objekto-adapterio pagalba. Pavyzdžiui, projektuojama POST sistema turi bendradarbiauti su keliomis išorinėmis tarnybomis, turinčiomis skirtingus interfeisus (kurių pačių keisti mes negalime): mokesčių apskaičiavimo sistema, mokėjimų autorizavimo sistema atsiskaitant kreditine kortele, buchalterinės apskaitos sistema, sandėlio apskaitos sistema, ...
6
Adapter Kaip POST sistemai bendrauti su įvairiomis, pavyzdžiui, mokesčių apskaičiavimo sistemomis, kurių kiekis gali ateityje augti (o gal ir mažėti)? Bandant realizuoti su jomis ryšį tiesiogiai, gautume painų kodą su sudėtingomis loginėmis konstrukcijomis (spageti kodas), kurį atsiradus bet kokiems tarnybų pokyčiams, turėtume keisti. Problema sprendžiama, kiekvienai išorinei mokesčių apskaičiavimo sistemai sukuriant atskirą objekto-adapterio egzempliorių. POST pranešimai mokesčių apskaičiavimo sistemų adapteriams peradresuojami polimorfiškai. Naudojant Adapterio šabloną, objekto-adapterio klasės pavadinime būtinai turi būti naudojamas žodis Adapter. Tai palengvina diagramų ar kodo supratimą pašaliniams asmenims.
7
Pavyzdys: mokesčių apskaičiavimo sistema
8
Pavyzdys: mokesčių apskaičiavimo sistema
Mokesčiams apskaičiuoti Sale kreipiasi (mokesciai()) į tuo metu naudojamą mokesčių apskaičiavimo sistemą, perduodama nuorodą į save kaip parametrą.
9
Klasių diagramos ir kodo ryšys
Kuriant Register egzempliorių nurodoma naudojama mokesčių sistema. Tai atliekama inicijuojant bazinio tipo MokesciuSistemaAdapter nuorodą mokesciuSistemaAdapter reikiamu mokesčių sistemos adapterio, pavyzdžiui, MokesciuSistema1Adapter egzemplioriumi. Vykdant sisteminę operaciją pradetiNaujaPirkima() sukuriamas naujas prekių krepšelis ir jam perduodama nuoroda į naudojamą mokesčių sistemos adapterį. Mokesčiams apskaičiuoti prekių krepšelis turi specialų metodą mokesciai(). Šiame metode PrekiuKrepselis MokesciuSistemaAdapter metodu mokesciai() kreipiasi į tuo metu naudojamą mokesčių apskaičiavimo sistemą, perduodamas nuorodą į save kaip parametrą.
10
Klasių diagramos ir kodo ryšys
Adapter šablono taikymas POST sistemoje Tai atliekama, pavyzdžiui, MokesciuSistema1Adapter egzemplioriuje randant tuo metu naudojamą mokesčių apskaičiavimo sistemą, pavyzdžiui, MokesciuSistema1 egzempliorių ir iškviečiant jo metodą mokesciai1(). Šiame metode iš prekių krepšelio paimamos visos jame esančios prekės, kiekvienai iš jų sukuriamas mokesčių MokamasMokestis egzempliorius ir įtraukiamas į grąžinamą mokamų mokesčių masyvą. Šiuo masyvu užpildomas atitinkamas mokamų mokesčių masyvas pačioje PrekiuKrepselis klasėje, taip sukuriant kompoziciją PrekiuKrepselis - MokamasMokestis. Pabaigoje, PrekiuKrepselis metode mokesciai() kiekvienos prekės mokesčiai sudedami ir bendra mokesčių suma grąžinama.
11
Factory Problema: kas turėtų sukurti objektą-adapterį?
Kaip nustatyti, koks bus sukuriamo objekto-adapterio tipas, pavyzdžiui, TaxMasterAdapter ar GoodGoldTaxProAdapter? T.y. kurį kursime ir naudosime? Ir kaip kursime? Sprendžiant, turi būti atsižvelgta į fundamentalų projektavimo principą – skirtingų sistemos aspektų funkcionalumo atskyrimą, kurio esmė yra sistemos suskaidymas į modulius sutinkamai su skirtingais sistemos funkcionavimo aspektais ir uždaviniais. Pavyzdžiui, reikia atskirti dalykinės srities programinius objektus atsakingus tik už taikymo logikos realizavimą, atskirti objektų grupes atsakingas už saveiką su išorinėmis tarnybomis ir t.t...
12
Factory Su objekto-adapterio sukūrimu susijusių klausimų sprendimo negalima patikėti dalykinės srities objektui, nes tai nėra susiję su taikymo logika. Sprendimas: šiems tikslams turi būti naudojamas tarnybinis objektas. Factory šablonas skirtas kurti tarpusavyje susijusių klasių grupių objektus, kūrimo metu nenurodant konkrečių kuriamų objektų klasių. Naudojant Factory šabloną, minėto tarnybinio objekto klasės pavadinime būtinai turi būti naudojamas žodis Factory. Factory objektas (ServicesFactory) projektuojamas taip, kad, pavyzdžiui, naujų tipų adapteriai galėtų būti kuriami nemodifikuojant fabriko klasės. Tam tereikia pakeisti nustatytos savybės reikšmę ir užtikrinti naujos adapterio klasės pasiekiamumą.
14
Klasių diagramos ir kodo ryšys
Užduotis: nustatyti norimos mokesčių sistemos adapterį, nurodant tik jos vardą - simbolių eilutę. Šiuo tikslu Register iškviečiamas PaslaugosFactory metodas gautiMokesciuSistemaAdapter(), kuriam per parametrus nurodomas, pavyzdžiui, klasės MokesciuSistema1Adapter vardas simbolių eilutės pavidale. Galimybė įvesti pasirenkamos mokesčių sistemos klasės vardą, vietoje išankstinio jos nurodymo programoje, žymiai padidina sistemos lankstumą.
15
Klasių diagramos ir kodo ryšys
Metode gautiMokesciuSistemaAdapter() pagal nurodytą vardą randamas atitinkamos klasės failas, sukuriamas jos objektas o jo nuoroda grąžinama kaip metodo rezultatas. Nurodžius kitos mokesčių sistemos klasės vardą, būtų analogiškai grąžinama nuoroda į jos egzempliorių. Tokiu būdu, Factory šablono pagalba galime lanksčiai kurti objektus, šiuo atveju MokesciuSistemaAdapter, net jei jie nežinomi sistemos kompiliavimo metu.
16
Singleton Problema: o kas turi sukurti patį fabriko objektą ir kaip jį pasiekti? Fabriko gali prireikti įvairiems objektams, todėl, kad nekiltų nevienareikšmiškumų, fabriko objektas sistemoje iš principo turi būti tik vienas ir kreiptis į jį turi būti galima tik vienu būdu ir globaliai. Perdavimą fabriko per parametrus gali būti sunku realizuoti, kartais peikiamas globalus pasiekiamumas yra labai patogus. Sprendimas: Singleton šablonas, objekto, kuris turi būti vienintelis programoje atveju, siūlo naudoti statinį jo klasės metodą grąžinantį vienintelį tokį objektą: Naudojame statinį kintamąjį, kuris statiniame metode inicijuojamas Singleton objektu tik tuomet jei yra null.
17
Class.forName(Y).newInstance()
Galime sukurti objektą be new sakinio, tiesiog iš baitkodų failo, nurodydami tik failo vardą Y.
18
Pavyzdys: buchalterinės apskaitos sistemos pasirinkimas
getInstance() yra numanomas ir nevaizduojamas public class Register{ public void initialize(){ ServicesFactory.getInstance().getAccountingAdapter(); ... } Singleton objektas (šiuo atveju fabrikas) prieinamas metodu SingletonClass.getInstance(). Po to, jam galima perduoti pranešimą SingletonClass.getInstance().doFo() norimiems veiksmams atlikti (esantiems metode doFo()).
19
Pastabos Vienintelio egzemplioriaus sukūrimui paprastai naudojama pasyvi inicializacija (lazy initialization). Ji turi privalumų prieš aktyvią inicializaciją (eager initialization). Vienintelio egzemplioriaus kūrimo procesas (taigi ir kompiuterio resursų naudojimas) nepradedamas tol kol išties neprireikia kreiptis į šį egzempliorių. Inicializacijai kartais reikalinga gana sudėtinga logika. public class ServicesFactory{ private static ServicesFactory instance = new ServicesFactory(); public static ServicesFactory getInstance(){ return instance; } ...
20
Kodėl visus tarnybinius metodus nepadarius statiniais?
Pavyzdžiui, gal ServicesFactory metodas getAccountAdapter galėtų būti statinis? Vienok, egzempliorių naudojimas turi savų privalumų: statiniai metodai nėra polimorfiniai (išskyrus programavimo kalboje SmallTalk), nutolusio iškvietimo mechanizmai (Java tai RMI) palaiko tik egzempliorių (t.y. nestatinius) metodus, egzempliorių panaudojimas yra lankstesnis: Singleton šablonas neleis, kad viename taikyme kokios tai klasės objektas būtų vienintelis, o kitame taikyme jau ne, Projektavimo pradžioje kartais atrodo, kad bus naudojamas tik vienas kokio tai objekto egzempliorius, o vėliau prireikia didesnio jų kiekio, taigi tenka modifikuoti praktiškai visą programą.
21
Factory ir Singleton šablonų taikymas POST sistemoje
22
Šablonų vardai ir idėjos leidžia abstrakčiau samprotauti ir aptarti projektinius sprendimus
Projektuotojai bendrauja savo idėjas konkretizuodami šablonų pagalba. Pavyzdžiui: “skirtingų interfeisų sų išorinėmis tarnybomis problemą spęsime naudodami Adapter šabloną”; “resursų adapterius kursime šablonų Singleton ir Factory pagalba”...
23
Strategy Problema: kaip panaudoti skirtingus algoritmus, turint omenyje jų pasikeitimo galimybę? Pavyzdžiui, prėkės kaina gali priklausyti nuo sezoninių nuolaidų, nuolaidų pastoviems klientams ir t.t. Nuolaidų skaičiavimų taisyklės (algoritmai) gali keistis, pavyzdžiui: 10% nuo prėkės kainos, 10 Lt nuo kiekvienos prėkės brangesnės nei 200 Lt ir t.t. Sprendimas: kiekvienam algoritmui (politikai, strategijai) naudoti atskirą (strategijos) klasę (tai didina klasių skaičių sistemoje) su polimorfiniu metodu. strategijos objektas turi būti susiejamas su konteksto objektu (context object), kuriam ir taikomas algoritmas (strategija). Pastaba: Dėl strategijų skirtumų, vienų strategijų metodai gali turėti nereikalingų kitų strategijų metodams parametrų.
24
Pavyzdys: kainų politika
Kiekvienam kainų apskaičiavimo algoritmui sukuriame savo strategijos klasę ...PricingStrategy, su polimorfiniu metodu getTotal. Konteksto objektas Sale, reikiamos nuolaidos apskaičiavimui iškviečia kurio tai strategijos objekto ...PricingStrategy metodą getTotal, perduodamas save patį parametru this. Strategijos objektas ...PricingStrategy metode getTotal naudoja konteksto objekto Sale nuorodą, kad galėtų iškviesti paties Sale metodą getPreDiscountTotal, grąžinantį bazinę prekių kainą, pagal kurią bus apskaičiuota reikiama nuolaida.
25
Strategijų klasės prekės kainos apskaičiavimui
26
Strategijos funkcionavimas
Pasiuntus objektui Sale pranešimą getTotal, Sale deleguoja dalį savo užduočių strategijos objektui (nuolaidų apskaičiavimą). Pastebime, kad konteksto objektas Sale savo srategijos objektui PercentDiscountPricingStrategy užtikrina savo paties pasiekiamumą parametrų pagalba.
27
Sprendimas
28
Pastaba: objektas Sale turi nuorodą ne į konkrečią klasę, o į jos bazinį tipą, pavyzdžiui, interfeisą IPricingStrategy. Tokiu būdu, šios nuorodos pagalba galime prieiti prie bet kokio strategijos objekto, kurio klasė realizuoja šį interfeisą. Pastaba: Visoms strategijoms naudojamas vienodas interfeisas, nepriklausomai nuo to ar jie sudėtingi ar paprasti.
29
Klasių diagramos ir kodo ryšys
Bazinės klasės KainuPolitikaStrategy pagalba galime pasirinkti reikiamą strategiją, pavyzdžiui, ProcentineKainuPolitikaStrategy arba SlenkstineKainuPolitikaStrategy egzempliorių. Prekių krepšelio kainai rasti, konteksto objekto PrekiuKrepselis metode gautiSuma() iškviečiamas, pavyzdžiui, ProcentineKainuPolitikaStrategy metodas gautiSuma(), perduodant save patį parametru this. Pastarąją aplinkybę vaizduojame klasių diagramoje priklausomybės ryšiu tarp konkrečių strategijų ir prekių krepšelio klasių.
30
Klasių diagramos ir kodo ryšys
Strategijos, pavyzdžiui, ProcentineKainuPolitikaStrategy metode gautiSuma() naudojama konteksto objekto PrekiuKrepselis nuorodą, kad būtų galima iškviesti pačios PrekiuKrepselis klasės metodą gautiPradineSuma(), grąžinantį bazinę prekių kainą, pagal kurią bus apskaičiuota reikiama nuolaida. Kartu, šiame metode gautiPradineSuma() nustatomas PrekiuKrepselis egzemplioriaus atributas pradineSuma naudojamas PrekiuKrepselis metode gautiSuma(). Grąžinama ProcentineKainuPolitikaStrategy metodo gautiSuma() pinigų suma - nuolaida bus toliau naudojama (kaip viena iš dalių) galutinei pirkinio kainai rasti.
31
Strategy šablono taikymas POST sistemoje
32
Strategijos objekto sukūrimas
Problema: kaip keisti kainų apskaičiavimo algoritmus? Objektiniame projektavime tai ekvivalentu klausimui “kas kurs konkrečių strategijų objektus?” Lanksčiam objektų kūrimui turime šabloną Factory. Analogiškai objektui ServicesFactory, turėsime objektą PricingStrategyFactory. Jam pakaks peduoti konkrečios srategijos klasės vardą ir kainų apskaičiavimo algoritmą bus galima keisti net programos darbo metu. Pastebime, kad kiekvienai objektų šeimai (išorinių tarnybų, kainų apskaičiavimo...) naudojamas atskiras Factory objektas.
33
Strategijos fabrikas Kaip ir dauguma fabrikų, PricingStrategyFactory
turi būti vieninteliu tokiu egzemplioriumi su globaliu priėjimu, todėl jam taikome Singleton šabloną.
34
Strategijos objekto sukūrimas
Sukūrus Sale egzempliorių jis kreipiasi į fabriką PricingStrategyFactory iš kurio gauna bazinio tipo, mūsų atveju, interfeiso IPricingStrategy nuorodą į konkretų strategijos objektą, mūsų atveju, PercentDiscountPricingStrategy (atitinkantį pasirinktą kainų apskaičiavimo algoritmą) kurį fabrikas ir sukuria.
35
Klasių diagramos ir kodo ryšys
PrekiuKrepselis egzempliorius kreipiasi į fabriko KainuPolitikaStrategyFactory metodą gautiKainuPolitikaStrategy() iš kurio gauna bazinio tipo, mūsų atveju, abstrakčiosios klasės KainuPolitikaStrategy nuorodą į konkretų, fabriko sukurtą strategijos objektą, pavyzdžiui, ProcentineKainuPolitikaStrategy. Pažymėtina, kad tarp PrekiuKrepselis ir KainuPolitikaStrategyFactory klasių yra abipusiai priklausomybės ryšiai: PrekiuKrepselis naudoja statinius KainuPolitikaStrategyFactory metodus, o pastarosios klasės egzempliorius gauna PrekiuKrepselis egzempliorių per metodo gautiKainuPolitikaStrategy() parametrą.
36
Composite Problema: Užtikrinti galimybę vienu metu taikyti įvairų kiekį skirtingų strategijų. Pavyzdžiui, taikoma tokia kainų politika: 20% nuolaida garbės piliečiams, 15% nuolaida pastoviems pirkėjams perkantiems prekių daugiau nei už 400 Lt, pirmadieniais 50 Lt nuolaida perkant prekes už sumą virš 500 Lt, perkant vieną pakuotę arbatos Darjeeling, pirkėjas įgyja 15% nuolaidą visoms kitoms prekėms. Tarkime, garbės pilietis perka pakuotę arbatos Darjeeling ir kitų prekių už 600 Lt. Kaip apskaičiuoti jam tenkančią nuolaidą?
37
Pastabos apie kainų politiką
Šio pavyzdžio strategijos skiriasi sekančiais faktoriais: laiku (pirmadienis...), pirkėjo tipu (garbės pilietis, pastovus pirkėjas...), pasirinkto produkto tipu (arbata Darjeeling) ir kiekiu... Šios problemos sprendimui parduotuvė suformuoja konfliktų sprendimo strategiją (conflict resolution strategy). Vienais atvejais gali būti naudojama didžiausios, kitais – mažiausios nuolaidos strategija. Taigi, vienu metu turime daug įvairių strategijų ir kiekvienam pirkėjui vienu metu gali būti taikomas skirtingas kiekis skirtingų strategijų. Kainų politikos taikymas priklauso nuo pirkėjo tipo, taigi, pirkėjo tipas turi būti žinomas objektui PricingStrategyFactory nuolaidos taikymo (konkrečios strategijos sukūrimo) perkamai prekei metu.
38
Composite taikymas Pastaba: Sale objektas neturi žinoti apie jam taikomų strategijų skaičių ir rūšis (nes jos pastoviai kinta ir jų kodas gali būti nežinomas), t.y. tai neturi būti jo klasės kode. Sprendimas: objektiniame projektavime sudėtingas elgesio būdas nustatomas kompozitinių ir paprastų objektų įdiegiančių tą patį interfeisą dariniu (objektu). Nuolaidos nustatymui jau turėjome interfeisą ISalePricingStrategy, tiktai dabar jame deklaruotas metodas getTotal turės grąžinti daugelio įvairiarūšių skaičiavimų rezultatą. Interfeisą ISalePricingStrategy, kaip ir anksčiau įdiegsime ne tik jau turėtais paprastais objektais-strategijomis PercentageDiscountPricingStrategy ir AbsoluteDiscountOverThresholdPricingStrategy, bei ir dar vienu kompozitiniu objektu-strategija CompositePricingStrategy.
40
Composite taikymas Kadangi nuolaidų strategijų gali būti įvairių, tai kompozitas CompositePricingStrategy bus viršklasė nuolaidos apskaičiavimo strategijų kompozitams, pvz.: CompositeBestForCustomerPricingStrategy ir CompositeBestForStorePricingStrategy. Galų gale, kompozitai-strategjjos yra sudaryti iš paprastų objektų-strategijų, kuriuos ir naudoja nuolaidos radimui. Pastebime, kad kompozitai turi (arba paveldi) atributą pricingStrategies, kuris yra nuoroda į visų ISalePricingStrategy objektų sąrašą. Tai skiriamasis kompozitinių objektų bruožas – išorinis kompozitas turi sąrašą vidinių objektų, lygiai taip pat pasiekiančių savo vidinius objektus, kaip ir išorinis kompozitas, ir t.t.
41
Composite taikymas Dabar su objektu Sale galima susieti kaip kompozitus, taip ir paprastus objektus objektui Sale tai atrodo kaip viena strategija, kadangi jam tai tiesiog objektas realizuojantis interfeisą ISalePricingStrategy ir “suprantantis” metodą getTotal(). Taigi, Sale gali naudoti bet kokį nuolaidų rinkinį, visai tuo “nesidomėdamas” ir nekeisdamas savo ryšio su nuolaidomis realizacijos. Pastaba: Composite šablono klasių objektams pasiekti yra naudojama rekursija.
42
Objektų sąveika pagal Composite šabloną
43
public abstract class CompositePricingStrategy
implements ISalePricingStrategy{ protected List pricingStrategies = new ArrayList(); public add(ISalePricingStrategy s){ pricingStrategies.add(s); } public abstract Money getTotal(Sale sale);
44
public class CompositeBestForCustomerPricingStrategy
extends CompositePricingStrategy { public Money getTotal(Sale sale){ Money lowestTotal=new Money(Integer.MAX_VALUE); for (Iterator i=pricingStrategies.iterator(); i.hasNext();){ ISalePricingStrategy strategy = (ISalePricingStrategy)i.next(); Money total = strategy.getTotal(sale); lowestTotal=total.min(lowestTotal); } return lowestTotal;
45
Kaip kurti strategijas?
Pirmiausiai sukuriamas kompozitas (pvz.: palankiausias pirkėjui, ar parduotuvei), kurį savo ruožtu sudaro einamosios nuolaidų strategijos (jau paprastos), pavyzdžiui, PercentageDiscountPricingStrategy jei nuolaidų nėra, tai procentinė nuolaida bus 0% Jei toliau prireiks taikyti naują nuolaidų strategiją, ją bus lengva pridėti prie kompozitinio objekto iš CompositePricingStrategy paveldėtu metodu add. Skirsime du strategijų kūrimo laiko atžvilgiu variantus: nuolaida nustatoma kuriant Sale egzempliorių, nuolaida nustatoma įvedant į POST sistemą pirkėjo tipą.
46
1. Nuolaida nustatoma kuriant Sale egzempliorių
strategijos klasės vardas gali būti įvedamas kaip sisteminė savybė, o procentinis nuolaidos dydis percent gali būti įvedamas JDBC pagalba.
47
2. Nuolaida nustatoma įvedant į POST sistemą pirkėjo tipą
Šiuo atveju būtina dar viena (penkta) sisteminė operacija enterCustomerForDiscount, atliekama po sisteminės operacijos endSale. Naujos sisteminės operacijos argumentu turi būti pirkėjo identifikatorius customerID. Įvedamas iš kortelės ar klaviatūra. Strategijos klasės vardas, pavyzdžiui PercentageDiscountPricingStrategy, gali būti įvedamas kaip sisteminė savybė;
48
Nuolaidų strategijos sukūrimas konkrečiam pirkėjo tipui
50
Klasių diagramos ir kodo ryšys
Register papildomas dar viena sistemine operacja ivestiKlientaNuolaidai(). Šiame metode, pagal per parametrus gautą kliento ID - simbolių eilutę pirkejasID nustatomas pirkėjas. Tuo tikslu kreipiamasi į klasės Parduotuve egzempliorių iškviečiant jo metodą gautiPirkejas(). Pastarasis metodas sukuria naują Pirkejas egzempliorių (turėtų sukurti pagal perduotą kliento ID). Pateiktame pavyzdyje sukuriamas "privelegijuotas" klientas.
51
Klasių diagramos ir kodo ryšys
Pabaigoje Register metode ivestiKlientaNuolaidai() iškviečiamas klasės PrekiuKrepselis metodas ivestiKlientaNuolaidai(), kuriam per parametrus perduodamas gautas iš parduotuvės Pirkejas egzempliorius. PrekiuKrepselis egzempliorius savo metode ivestiKlientaNuolaidai() kreipiasi į fabriko KainuPolitikaStrategyFactory metodą gautiKainuPolitikaStrategy() iš kurio gauna bazinio tipo, mūsų atveju, abstrakčiosios klasės KainuPolitikaStrategy nuorodą (mūsų pavyzdyje) į strategijos kompozitą CompositePalankiausiaPirkejuiKainuPolitikaStrategy.
52
Klasių diagramos ir kodo ryšys
Toliau pastajame KainuPolitikaStrategyFactory metode kainų politikos strategijos CompositeKainuPolitikaStrategy metodu add() konkrečių strategijų ProcentineKainuPolitikaStrategy ir SlenkstineKainuPolitikaStrategy egzemplioriai pridedami prie bazinės klasės KainuPolitikaStrategy kolekcijos saugančios KainuPolitikaStrategy egzempliorius. Pabaigoje nuoroda į kompozitą grąžinama prekių krepšeliui ir inicijuoja jo nuorodą kainuPolitikaStrategy.
53
Klasių diagramos ir kodo ryšys
Dabar galutinei pirkinio sumai apskaičiuoti PrekiuKrepselis metode gautiSuma() per nuorodą kainuPolitikaStrategy bus iškviečiamas CompositePalankiausiaPirkejuiKainuPolitikaStrategy metodas gautiSuma(). Pastarajame metode bus perrinktos konkrečios strategijos, iškviesti jų metodai gautiSuma() ir pagal CompositePalankiausiaPirkejuiKainuPolitikaStrategy kainų politiką bus pasirinktas tinkamiausias konkrečios strategijos grąžinamas rezultatas.
54
Composite šablono taikymas POST sistemoje
55
Kodėl ne Register nurodo objektui PricingStrategyFactory sukurti naują strategiją ir neperduoda jos po to objektui Sale? Tai apsprendžia: šablonas Low Coupling Objektas Sale jau ir taip susietas su objektu-fabriku PricingStrategyFactory. Jei su objektu-fabriku būtų susietas ir Register, tai surištumas sistemoje išaugtų. šablonas Expert Objektas Sale yra ekspertas einamosios kainų politikos (jis ją taiko) klausimais (o ji gali keistis), todėl jam ir turi būti priskirta ši pareiga.
56
Pastaba: ID to Objects Identifikatorius customerID transformuojamas į objektą Customer, kai Register nurodo Store gauti pirkėją pagal jo identifikatorių. Store ši pareiga priskiriama pagal Expert šabloną. Šiam tikslui užklausą siunčia ne Sale, o Register, kadangi jis turi ryšį per atributą, be to priešingu atveju, Sale turėtų turėti nuorodą į Store, kas nepageidautina pagal Low Coupling šabloną. Objektiniame projektavime raktai ir identifikatoriai kaip taisyklė transformuojami į objektus. Toks sprendimas padidina sistemos lankstumą – pavyzdžiui Customer gali turėti papildomą informaciją apie pirkėją.
57
Pastaba: agregato perdavimas per parametrus
Pranešimu addCustomerPricingStrategy, objektui-fabrikui per parametrus perduodamas objektas Sale ir tik po to objektas-fabrikas iš objekto Sale gauna objektus Customer ir PricingStrategy. Kodėl jie neperduodami atskirai vietoje Sale? Agregatą sudarantys objektai paprastai neišskiriami ir atskirai neperduodami: toks sprendimas paprastesnis, padidėja sistemos lankstumas, kadangi Sale gali valdyti šį informacijos perdavimo procesą.
58
Facade Problema: ką daryti su papildomomis biznio taisyklėmis, skirtomis sisteminių operacijų modifikavimui? Pavyzdžiui: Tarkime, yra galimi dovanų sertifikatai. Dažniausiai juo galima įsigyti tik vieną prekę. Tuomet visos operacijos enterItem, išskyrus pirmąją, turi būti atšaukiamos. Jei dovanų sertifikatu įsigyjant prekę kasininkas užklaustų apie grąžos skaičiavimą, tokia užklausa turėtų būti atšaukta. Tarkime, pirkinį galima apiforminti kaip parduotuvės labdaros akciją. Tokiose akcijose prekės kaina neturėtų viršyti, pavyzdžiui, 250 Lt, be to, pardavėju turėtų užsiregistruoti įgaliotas parduotuvės menedžeris.
59
Facade Papildomų biznio taisyklių realizavimas turėtų minimaliai paveikti jau esančius programinius komponentus. Sprendimas: sąveikai su sistema naudojame objektą-fasadą, pavyzdžiui, POSRuleEngineFacade užtikrinantį vieningą interfeisą ir atsakingą už sąveiką su sistemos komponentais; priežastis: programiniai komponentai paprastai turi labai įvairius interfeisus, todėl tiesioginė sąveika su jais nepageidautina. Pastebėsime, kad objekto-fasado kūrimui paprastai naudojamas Singleton šablonas.
60
Į objektą-fasadą kreipiamės prieš iškviesdami metodą, kurio
veikimą norime modifikuoti. Pavyzdžiui, šiuo atveju, sprendžiama ar galima įsigyti dar vieną prekę. public class Sale{ public void makeLineItem(ProductSpecification spec, int quantity){ SalesLineItem sli=new SalesLineItem(spec,quantity); if(POSRuleEngineFacade.getInstance(). isInvalid(sli,this)) return; llineItems.add(sli); } ... Už fasado gali būti labai sudėtinga posistemė su daugybe objektų, ar gal ten aplamai ne objektinis kodas. Fasadas slepia visą posistemės realizacijos sudėtingumą.
61
<<priklausomybė>>
62
Klasių diagramos ir kodo ryšys
PrekiuKrepselis metode ivestiPreke() patikriname ar gali būti parduodama prekė. Tam metodu gautiEgzempliorius() gaunamas vienintelis POSTaisyklesFacade egzempliorius, kuriam iškviečiamas jo metodas negalioja(). Pastarasis metodas nustato, ar pirkėjas yra privelegijuotas ir jei ne - grąžinama false. Pastaruoju atveju klasės PrekiuKrepselis metodas ivestiPreke() gali toliau tęsti savo veiklą, kurios tikslas yra įdėti prekę į krepšelį.
63
Klasių diagramos ir kodo ryšys
Dar vienas patikrinimas - prekei "preke2", lengvata netaikoma. POSTaisyklesFacade metodas negalioja() grąžins true, visoms ne "preke2" parduodamoms prekėms. Taigi, išskyrus prekę "preke2", kitoms prekėms bus grąžinama false ir PrekiuKrepselis metodas ivestiPreke() galės toliau tęsti savo veiklą - įdėti tokias prekes į krepšelį.
64
Facade šablono taikymas POST sistemoje
65
Observer Problema: kaip atnaujinti GVS langą, pasikeitus pirkinio kainai? Kodėl pats Sale objektas (Subject) negalėtų pasiųsti atitinkamo pranešimo langui? Duomenų sluoksnio objektai neturi siųsti pranešimų GVS objektams – taip užtikrinamas silpnas jų surištumas, t.y. galima bus pakeisti GVS neliečiant biznio logikos ir atvirkščiai. Sprendimas: naudoti įvykių klausytojo interfeisą. Objektai klausytojai (Observer) realizuoja šį interfeisą ir dinamiškai registruojasi tokių įvykių klausytojais. Įvykus tokiam įvykiui, apie tai informuojami visi tokio įvykio klausytojai (per metodus kurių antraštės aprašytos ne Observer klasėje, o interfeise), dėka ko Observer objektai iškviečia Subject objektų metodus per Subject tipo nuorodą.
66
GVS lango atnaujinimas pasikeitus pirkinio kainai
67
Pavyzdys Aprašomas interfeisas PropertyListener su metodu onPropertyEvent. Aprašomas langas SaleFrame1 realizuojantis (susietas su interfeisu realizacijos ryšiu) šio interfeiso metodą onPropertyEvent. Inicijuojant langą SaleFrame1 jam perduodama nuoroda į Sale egzempliorių prie kurio registruosimės. Lango objektas SaleFrame1 registruojasi Sale egzemplioriaus įvykių klausytoju. Dabar pasikeitus pirkinio kainai langas bus apie tai informuojamas ir galės ją atvaizduoti. Pastebėsime, kad Sale objektas nesusijęs su GVS langu (taigi jį galima nepriklausomai keisti), jis susijęs tik su interfeisu PropertyListener. Objektas Sale apie pirkinio kainos pasikeitimus informuoja visus PropertyListener klausytojus (jei tokių yra).
69
Įvykių klausytojas SaleFrame1 registruojasi gauti informaciją apie objekto Sale savybių pasikeitimus
70
Pasikeitus Sale pirkinio kainai, ši informacija perduodama visiems tokių įvykių klausytojams pranešimu onPropertyEvent
71
SaleFrame1, realizuojantis interfeisą PropertyListener,
realizuoja ir jo metodą onPropertyEvent. Gavęs šį pranešimą, jis savo ruožtu siunčia pranešimą JTextField egzemplioriui apie pirkinio kainos atnaujinimą.
72
Klasių diagramos ir kodo ryšys
Pateikiamame pavyzdyje klasė Klientas atstovauja ne tik grafinę vartotojo sąsają, bet ir visą pirkimo procesą. Pastarasis yra modeliuojamas šios klasės metode klientoVeikla(). Observer šablono požiūriu svarbus yra kliento (Observer) prisiregistravimas prie prekių krepšelio (Subject). Tam iškviečiami klasės PrekiuKrepselis priregistravimo metodai priregistruotiTarpineSumaListener() ir priregistruotiGrazaListener(). Interfeisas TarpineSumaListener pateikia metodą tarpineSumaEvent() kurį iškviečia klasės PrekiuKrepselis metodas informuotiTarpineSumaListener(). Šis metodas informuoja visus aukščiau minėtus prisiregistravusius klausytojus (šiuo atveju klientą) įdiegiančius interfeisą TarpineSumaListener.
73
Klasių diagramos ir kodo ryšys
Savo ruožtu šis metodas iškviečiamas kaskart įvedus naują prekę, t.y. kai Register objektas iškviečia PrekiuKrepselis metodą ivestiPreke() ir pirkimo pabaigoje, kai Register objekto metode pirkimoPabaiga() iškviečiamas PrekiuKrepselis metodas gautiSuma() galutinei pirkinio sumai pateikti. Analogiškai interfeisas GrazaListener aprašo metodo grazaEvent() antraštę, kurį iškviečia klasės PrekiuKrepselis metodas informuotiGrazaListener(). Šis metodas informuoja aukščiau minėtą prisiregistravusį klausytoją (šiuo atveju klientą) , įdiegiantį interfeisą GrazaListener. Savo ruožtu šis metodas yra iškviečiamas Register objekto metode uzmokestis(), kai reikia pateikti grąžos sumą, t.y. kai iškviečiamas klasės PrekiuKrepselis metodas uzmokestis().
74
Observer šablono taikymas POST sistemoje
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.