Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00588 007675 12253316 na godz. na dobę w sumie
Wzorce projektowe. Elementy oprogramowania obiektowego wielokrotnego użytku - ebook/pdf
Wzorce projektowe. Elementy oprogramowania obiektowego wielokrotnego użytku - ebook/pdf
Autor: , , , Liczba stron: 376
Wydawca: Helion Język publikacji: polski
ISBN: 978-83-283-3314-7 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> programowanie >> wzorce projektowe
Porównaj ceny (książka, ebook (-20%), audiobook).

Naucz się wykorzystywać wzorce projektowe i ułatw sobie pracę!

Projektowanie oprogramowania obiektowego nie jest łatwe, a przy założeniu, że powinno ono nadawać się do wielokrotnego użytku, staje się naprawdę skomplikowane. Aby stworzyć dobry projekt, najlepiej skorzystać ze sprawdzonych i efektywnych rozwiązań, które wcześniej były już stosowane. W tej książce znajdziesz właśnie najlepsze doświadczenia z obszaru programowania obiektowego, zapisane w formie wzorców projektowych gotowych do natychmiastowego użycia!

W książce „Wzorce projektowe. Elementy oprogramowania obiektowego wielokrotnego użytku” opisano, czym są wzorce projektowe, a także w jaki sposób pomagają one projektować oprogramowanie obiektowe. Podręcznik zawiera studia przypadków, pozwalające poznać metody stosowania wzorców w praktyce. Zamieszczono tu również katalog wzorców projektowych, podzielony na trzy kategorie: wzorce konstrukcyjne, strukturalne i operacyjne. Dzięki temu przewodnikowi nauczysz się skutecznie korzystać z wzorców projektowych, ulepszać dokumentację i usprawniać konserwację istniejących systemów. Krótko mówiąc, poznasz najlepsze sposoby sprawnego opracowywania niezawodnego projektu.

Wykorzystaj zestaw konkretnych narzędzi do programowania obiektowego!


Dr Erich Gamma jest dyrektorem technicznym w Software Technology Center of Object Technology International w Zurychu (Szwajcaria).
Dr Richard Helm jest członkiem zespołu Object Technology Practice Group w IBM Consulting Group w Sydney (Australia).
Dr Ralph Johnson jest pracownikiem naukowym na wydziale nauk komputerowych Uniwersytetu Illinois w Urbana-Champaign.
Dr John Vlissider prowadzi badania w Thomas J. Watson Research Center firmy IBM w Hawthorne w stanie Nowy Jork.
Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

Tytuł oryginału: Design Patterns Tłumaczenie: Tomasz Walczak Projekt okładki: Studio Gravite / Olsztyn; Obarek, Pokoński, Pazdrijowski, Zaprucki ISBN: 978-83-283-3313-0 Authorized translation from the English language edition, entitled: Design Patterns, First Edition, ISBN 0201633612, by Erich Gamma; and Richard Helm, published by Pearson Education, Inc, publishing as Addison-Wesley Professional; Copyright © 1995 by Addison-Wesley. All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from Pearson Education Inc. Material from A Pattern Language: Towns/Buildings/Construction by Christopher Alexander, copyright © 1977 by Christopher Alexander is reprinted by permission of Oxford University Press, Inc. Polish language edition published by Helion SA Copyright © 2010, 2017 Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną, fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje naruszenie praw autorskich niniejszej publikacji. Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich właścicieli. Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce informacje były kompletne i rzetelne. Nie biorą jednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz Wydawnictwo HELION nie ponoszą również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji zawartych w książce. Materiały graficzne na okładce zostały wykorzystane za zgodą iStockPhoto Inc. Wydawnictwo HELION ul. Kościuszki 1c, 44-100 GLIWICE tel. 32 231 22 19, 32 230 98 63 e-mail: helion@helion.pl WWW: http://helion.pl (księgarnia internetowa, katalog książek) Drogi Czytelniku! Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres http://helion.pl/user/opinie/wzoelv Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję. Printed in Poland. • Kup książkę • Poleć książkę • Oceń książkę • Księgarnia internetowa • Lubię to! » Nasza społeczność SPIS TRE¥CI Rozdziaï 1. Rozdziaï 2. Rozdziaï 3. Przedmowa ............................................................................................................................. 9 WstÚp ..................................................................................................................................... 11 Przewodnik dla Czytelników ............................................................................................ 13 Wprowadzenie ..................................................................................................................... 15 1.1. Czym jest wzorzec projektowy? ................................................................................. 16 1.2. Wzorce projektowe w architekturze MVC w jÚzyku Smalltalk ............................. 18 1.3. Opisywanie wzorców projektowych ......................................................................... 20 1.4. Katalog wzorców projektowych ................................................................................. 22 1.5. Struktura katalogu ........................................................................................................ 24 1.6. Jak wzorce pomagajÈ rozwiÈzaÊ problemy projektowe? ....................................... 26 1.7. Jak wybraÊ wzorzec projektowy? ............................................................................... 42 1.8. Jak stosowaÊ wzorce projektowe? .............................................................................. 43 Studium przypadku — projektowanie edytora dokumentów ................................... 45 2.1. Problemy projektowe ................................................................................................... 45 2.2. Struktura dokumentu ................................................................................................... 47 2.3. Formatowanie ................................................................................................................ 52 2.4. Ozdabianie interfejsu uĝytkownika ........................................................................... 55 2.5. Obsïuga wielu standardów wyglÈdu i dziaïania ..................................................... 59 2.6. Obsïuga wielu systemów okienkowych .................................................................... 63 2.7. Dziaïania uĝytkowników ............................................................................................. 69 2.8. Sprawdzanie pisowni i podziaï sïów ......................................................................... 74 2.9. Podsumowanie .............................................................................................................. 86 Wzorce konstrukcyjne ........................................................................................................ 87 BUDOWNICZY (BUILDER) .......................................................................................92 FABRYKA ABSTRAKCYJNA (ABSTRACT FACTORY) .....................................101 METODA WYTWÓRCZA ........................................................................................110 PROTOTYP (PROTOTYPE) .....................................................................................120 SINGLETON (SINGLETON) ....................................................................................130 Omówienie wzorców konstrukcyjnych ......................................................................... 137 Kup książkęPoleć książkę 8 SPIS TRE¥CI Rozdziaï 4. Rozdziaï 5. Rozdziaï 6. Dodatek A Dodatek B Dodatek C Wzorce strukturalne .......................................................................................................... 139 ADAPTER (ADAPTER) ............................................................................................141 DEKORATOR (DECORATOR) ................................................................................152 FASADA (FACADE) .................................................................................................161 KOMPOZYT (COMPOSITE) ....................................................................................170 MOST (BRIDGE) .......................................................................................................181 PEàNOMOCNIK (PROXY) .......................................................................................191 PYàEK (FLYWEIGHT) .............................................................................................201 Omówienie wzorców strukturalnych ............................................................................. 213 Wzorce operacyjne ............................................................................................................ 215 INTERPRETER (INTERPRETER) ............................................................................217 ITERATOR (ITERATOR) ..........................................................................................230 àAēCUCH ZOBOWIĄZAē (CHAIN OF RESPONSIBILITY) ...................................244 MEDIATOR (MEDIATOR) .......................................................................................254 METODA SZABLONOWA (TEMPLATE METHOD) ............................................264 OBSERWATOR (OBSERVER) .................................................................................269 ODWIEDZAJĄCY (VISITOR) ..................................................................................280 PAMIĄTKA (MEMENTO) ........................................................................................294 POLECENIE (COMMAND) ......................................................................................302 STAN (STATE) ..........................................................................................................312 STRATEGIA (STRATEGY) ......................................................................................321 Omówienie wzorców operacyjnych ............................................................................... 330 Podsumowanie ................................................................................................................... 335 6.1. Czego moĝna oczekiwaÊ od wzorców projektowych? ......................................... 335 6.2. Krótka historia ............................................................................................................. 339 6.3. SpoïecznoĂÊ zwiÈzana ze wzorcami ......................................................................... 340 6.4. Zaproszenie .................................................................................................................. 342 6.5. Sïowo na zakoñczenie ................................................................................................ 342 Sïowniczek .......................................................................................................................... 343 Przewodnik po notacji ...................................................................................................... 347 B.1. Diagram klas ................................................................................................................ 347 B.2. Diagram obiektów ...................................................................................................... 349 B.3. Diagram interakcji ...................................................................................................... 350 Klasy podstawowe ............................................................................................................. 351 C.1. List ................................................................................................................................. 351 C.2. Iterator .......................................................................................................................... 354 C.3. ListIterator ................................................................................................................... 354 C.4. Point .............................................................................................................................. 355 C.5. Rect ............................................................................................................................... 355 Bibliografia ......................................................................................................................... 357 Skorowidz ........................................................................................................................... 363 Kup książkęPoleć książkę ROZDZIA’ 3. Wzorce konstrukcyjne Konstrukcyjne wzorce projektowe pozwalajÈ ujÈÊ w abstrakcyjnej formie proces tworzenia egzemplarzy klas. PomagajÈ zachowaÊ niezaleĝnoĂÊ systemu od sposobu tworzenia, skïadania i reprezentowania obiektów. Klasowe wzorce konstrukcyjne sÈ oparte na dziedziczeniu i sïuĝÈ do modyfikowania klas, których egzemplarze sÈ tworzone. W obiektowych wzorcach kon- strukcyjnych tworzenie egzemplarzy jest delegowane do innego obiektu. Wzorce konstrukcyjne zyskujÈ na znaczeniu wraz z coraz czÚstszym zastÚpowaniem w syste- mach dziedziczenia klas skïadaniem obiektów. Powoduje to, ĝe programiĂci kïadÈ mniejszy nacisk na trwaïe zapisywanie w kodzie okreĂlonego zestawu zachowañ, a wiÚkszy — na defi- niowanie mniejszego zbioru podstawowych dziaïañ, które moĝna poïÈczyÊ w dowolnÈ liczbÚ bardziej zïoĝonych zachowañ. Dlatego tworzenie obiektów o okreĂlonych zachowaniach wy- maga czegoĂ wiÚcej niĝ prostego utworzenia egzemplarza klasy. We wzorcach z tego rozdziaïu powtarzajÈ siÚ dwa motywy. Po pierwsze, wszystkie te wzorce kapsuïkujÈ informacje o tym, z których klas konkretnych korzysta system. Po drugie, ukry- wajÈ proces tworzenia i skïadania egzemplarzy tych klas. System zna tylko interfejsy obiektów zdefiniowane w klasach abstrakcyjnych. Oznacza to, ĝe wzorce konstrukcyjne dajÈ duĝÈ ela- stycznoĂÊ w zakresie tego, co jest tworzone, kto to robi, jak przebiega ten proces i kiedy ma miejsce. UmoĝliwiajÈ skonfigurowanie systemu z obiektami-produktami o bardzo zróĝnicowanych strukturach i funkcjach. Konfigurowanie moĝe przebiegaÊ statycznie (w czasie kompilacji) lub dynamicznie (w czasie wykonywania programu). Niektóre wzorce konstrukcyjne sÈ dla siebie konkurencjÈ. Na przykïad w niektórych warunkach moĝna z poĝytkiem zastosowaÊ zarówno wzorzec Prototyp (s. 120), jak i Fabryka abstrakcyjna (s. 101). W innych przypadkach wzorce siÚ uzupeïniajÈ. We wzorcu Budowniczy (s. 92) moĝna wykorzystaÊ jeden z pozostaïych wzorców do okreĂlenia, które komponenty zostanÈ zbudowane, a do zaimplementowania wzorca Prototyp (s. 120) moĝna uĝyÊ wzorca Singleton (s. 130). Poniewaĝ wzorce konstrukcyjne sÈ mocno powiÈzane ze sobÈ, przeanalizujemy caïÈ ich piÈtkÚ razem, aby podkreĂliÊ podobieñstwa i róĝnice miÚdzy nimi. Wykorzystamy teĝ jeden przykïad do zilustrowania implementacji tych wzorców — tworzenie labiryntu na potrzeby gry kom- puterowej. Labirynt i gra bÚdÈ nieco odmienne w poszczególnych wzorcach. Czasem celem gry bÚdzie po prostu znalezienie wyjĂcia z labiryntu. W tej wersji gracz prawdopodobnie bÚdzie Kup książkęPoleć książkę 88 Rozdziaï 3. • WZORCE KONSTRUKCYJNE widziaï tylko lokalny fragment labiryntu. Czasem w labiryntach trzeba bÚdzie rozwiÈzaÊ pro- blemy i poradziÊ sobie z zagroĝeniami. W tych odmianach moĝna udostÚpniÊ mapÚ zbadanego juĝ fragmentu labiryntu. Pominiemy wiele szczegóïów dotyczÈcych tego, co moĝe znajdowaÊ siÚ w labiryncie i czy gra jest jedno-, czy wieloosobowa. Zamiast tego skoncentrujemy siÚ na tworzeniu labiryntów. Labirynt definiujemy jako zbiór pomieszczeñ. Kaĝde z nich ma informacje o sÈsiadach. MogÈ to byÊ nastÚpne pokoje, Ăciana lub drzwi do innego pomieszczenia. Klasy Room, Door i Wall reprezentujÈ komponenty labiryntu uĝywane we wszystkich przykïa- dach. Definiujemy tylko fragmenty tych klas potrzebne do utworzenia labiryntu. Ignorujemy graczy, operacje wyĂwietlania labiryntu i poruszania siÚ po nim oraz inne waĝne funkcje nie- istotne przy generowaniu labiryntów. Poniĝszy diagram ilustruje relacje miÚdzy wspomnianymi klasami: Kaĝde pomieszczenie ma cztery strony. W implementacji w jÚzyku C++ do okreĂlania stron póïnocnej, poïudniowej, wschodniej i zachodniej sïuĝy typ wyliczeniowy Direction: enum Direction {North, South, East, West}; W implementacji w jÚzyku Smalltalk kierunki te sÈ reprezentowane za pomocÈ odpowiednich symboli. MapSite to klasa abstrakcyjna wspólna dla wszystkich komponentów labiryntu. Aby uproĂciÊ przykïad, zdefiniowaliĂmy w niej tylko jednÈ operacjÚ — Enter. Jej dziaïanie zaleĝy od tego, gdzie gracz wchodzi. JeĂli jest to pomieszczenie, zmienia siÚ lokalizacja gracza. Jeĝeli sÈ to drzwi, mogÈ zajĂÊ dwa zdarzenia — jeĂli sÈ otwarte, gracz przejdzie do nastÚpnego pokoju, a o za- mkniÚte drzwi uĝytkownik rozbije sobie nos. class MapSite { public: virtual void Enter() = 0; }; Enter to prosty podstawowy element bardziej zïoĝonych operacji gry. Na przykïad jeĂli gracz znajduje siÚ w pomieszczeniu i zechce pójĂÊ na wschód, gra moĝe ustaliÊ, który obiekt MapSite znajduje siÚ w tym kierunku, i wywoïaÊ operacjÚ Enter tego obiektu. Operacja Enter specyficzna Kup książkęPoleć książkę WZORCE KONSTRUKCYJNE 889 dla podklasy okreĂli, czy gracz zmieniï lokalizacjÚ czy rozbiï sobie nos. W prawdziwej grze operacja Enter mogïaby przyjmowaÊ jako argument obiekt reprezentujÈcy poruszajÈcego siÚ gracza. Room to podklasa konkretna klasy MapSite okreĂlajÈca kluczowe relacje miÚdzy komponenta- mi labiryntu. Przechowuje referencje do innych obiektów MapSite i numer pomieszczenia (numery te sïuĝÈ do identyfikowania pokojów w labiryncie). class Room : public MapSite { public: Room(int roomNo); MapSite* GetSide(Direction) const; void SetSide(Direction, MapSite*); virtual void Enter(); private: MapSite* _sides[4]; int _roomNumber; }; Poniĝsze klasy reprezentujÈ ĂcianÚ i drzwi umieszczone po dowolnej stronie pomieszczenia. class Wall : public MapSite { public: Wall(); virtual void Enter(); }; class Door : public Mapsite { public: Door(Room* = 0, Room* = 0); virtual void Enter(); Room* OtherSideFrom(Room*); private: Room* _room1; Room* _room2; bool _isOpen; }; Potrzebne sÈ informacje nie tylko o czÚĂciach labiryntu. Zdefiniujemy teĝ klasÚ Maze repre- zentujÈcÈ kolekcjÚ pomieszczeñ. Klasa ta udostÚpnia operacjÚ RoomNo, która znajduje okreĂlony pokój po otrzymaniu jego numeru. class Mase { public: Maze(); void AddRoom(Room*); Kup książkęPoleć książkę 90 Rozdziaï 3. • WZORCE KONSTRUKCYJNE Room* RoomNo(int) const; private: // ... }; Operacja RoomNo moĝe znajdowaÊ pomieszczenia za pomocÈ wyszukiwania liniowego, tablicy haszujÈcej lub prostej tablicy. Nie bÚdziemy jednak zajmowaÊ siÚ takimi szczegóïami. Zamiast tego skoncentrujmy siÚ na tym, jak okreĂliÊ komponenty obiektu Maze. NastÚpnÈ klasÈ, jakÈ zdefiniujemy, jest MazeGame. Sïuĝy ona do tworzenia labiryntu. Prostym sposobem na wykonanie tego zadania jest uĝycie serii operacji dodajÈcych komponenty do la- biryntu i ïÈczÈcych je. Na przykïad poniĝsza funkcja skïadowa utworzy labirynt skïadajÈcy siÚ z dwóch pomieszczeñ rozdzielonych drzwiami: Maze* MazeGame::CreateMaze () { Maze* aMaze = new Maze; Room* r1 = new Room(1); Room* r2 = new Room(2); Door* theDoor = new Door(r1, r2); aMaze- AddRoom(r1); aMaze- AddRoom(r2); r1- SetSide(North, new Wall); r1- SetSide(East, theDoor); r1- SetSide(South, new Wall); r1- SetSide(West, new Wall); r2- SetSide(North, new Wall); r2- SetSide(East, new Wall); r2- SetSide(South, new Wall); r2- SetSide(West, theDoor); return aMaze; } Funkcja ta jest stosunkowo skomplikowana, jeĂli weěmiemy pod uwagÚ, ĝe jedyne, co robi, to tworzy labirynt skïadajÈcy siÚ z dwóch pomieszczeñ. Moĝna ïatwo wymyĂliÊ sposób na uproszczenie tej funkcji. Na przykïad konstruktor klasy Room mógïby inicjowaÊ pokój przez przypisanie Ăcian do jego stron. Jednak to rozwiÈzanie powoduje jedynie przeniesienie kodu w inne miejsce. Prawdziwy problem zwiÈzany z tÈ funkcjÈ skïadowÈ nie jest zwiÈzany z jej rozmiarem, ale z brakiem elastycznoĂci. Powoduje ona zapisanie na staïe ukïadu labiryntu. Zmiana tego ukïadu wymaga zmodyfikowania omawianej funkcji skïadowej. Moĝna to zrobiÊ albo przez jej przesïoniÚcie (co oznacza ponownÈ implementacjÚ caïego kodu), albo przez zmodyfikowanie jej fragmentów (to podejĂcie jest naraĝone na bïÚdy i nie sprzyja ponownemu wykorzystaniu rozwiÈzania). Wzorce konstrukcyjne pokazujÈ, jak zwiÚkszyÊ elastycznoĂÊ projektu. Nie zawsze oznacza to zmniejszenie samego projektu. Wzorce te przede wszystkim uïatwiajÈ modyfikowanie klas definiujÈcych komponenty labiryntu. Kup książkęPoleć książkę WZORCE KONSTRUKCYJNE 991 WZORCE KONSTRUKCYJNE Zaïóĝmy, ĝe chcemy powtórnie wykorzystaÊ ukïad labiryntu w nowej grze obejmujÈcej (miÚ- dzy innymi) magiczne labirynty. Potrzebne bÚdÈ w niej nowe rodzaje komponentów, takie jak DoorNeedingSpell (drzwi, które moĝna zamknÈÊ i nastÚpnie otworzyÊ tylko za pomocÈ czaru) i EnchantedRoom (pokój z niezwykïymi przedmiotami, na przykïad magicznymi kluczami lub czarami). Jak moĝna w ïatwy sposób zmodyfikowaÊ operacjÚ CrateMaze, aby tworzyïa labi- rynty z obiektami nowych klas? W tym przypadku najwiÚksza przeszkoda zwiÈzana jest z zapisaniem na staïe klas, których egzemplarze tworzy opisywana operacja. Wzorce konstrukcyjne udostÚpniajÈ róĝne sposoby usuwania bezpoĂrednich referencji do klas konkretnych z kodu, w którym trzeba tworzyÊ egzemplarze takich klas: Ź JeĂli operacja CreateMaze przy tworzeniu potrzebnych pomieszczeñ, Ăcian i drzwi wywo- ïuje funkcje wirtualne zamiast konstruktora, moĝna zmieniÊ klasy, których egzemplarze powstajÈ, przez utworzenie podklasy klasy MazeGame i ponowne zdefiniowanie funkcji wirtualnych. To rozwiÈzanie to przykïad zastosowania wzorca Metoda wytwórcza (s. 110). Ź JeĂli operacja CreateMaze otrzymuje jako parametr obiekt, którego uĝywa do tworzenia pomieszczeñ, Ăcian i drzwi, moĝna zmieniÊ klasy tych komponentów przez przekazanie nowych parametrów. Jest to przykïad zastosowania wzorca Fabryka abstrakcyjna (s. 101). Ź JeĂli operacja CreateMaze otrzymuje obiekt, który potrafi utworzyÊ caïy nowy labirynt za pomocÈ operacji dodawania pomieszczeñ, drzwi i Ăcian, moĝna zastosowaÊ dziedziczenie do zmodyfikowania fragmentów labiryntu lub sposobu jego powstawania. W ten sposób dziaïa wzorzec Budowniczy (s. 92). Ź JeĂli operacja CreateMaze jest sparametryzowana za pomocÈ róĝnych prototypowych obiektów reprezentujÈcych pomieszczenia, drzwi i Ăciany, które kopiuje i dodaje do labi- ryntu, moĝna zmieniÊ ukïad labiryntu przez zastÈpienie danych obiektów prototypowych innymi. Jest to przykïad zastosowania wzorca Prototyp (s. 120). Ostatni wzorzec konstrukcyjny, Singleton (s. 130), pozwala zagwarantowaÊ, ĝe w grze po- wstanie tylko jeden labirynt, a wszystkie obiekty gry bÚdÈ mogïy z niego korzystaÊ (bez ucie- kania siÚ do stosowania zmiennych lub funkcji globalnych). Wzorzec ten uïatwia teĝ rozbu- dowywanie lub zastÚpowanie labiryntów bez modyfikowania istniejÈcego kodu. Kup książkęPoleć książkę 92 Rozdziaï 3. • WZORCE KONSTRUKCYJNE BUDOWNICZY (BUILDER) obiektowy, konstrukcyjny PRZEZNACZENIE Oddziela tworzenie zïoĝonego obiektu od jego reprezentacji, dziÚki czemu ten sam proces konstrukcji moĝe prowadziÊ do powstawania róĝnych reprezentacji. UZASADNIENIE Czytnik dokumentów w formacie RTF (ang. Rich Text Format) powinien móc przeksztaïcaÊ takie dokumenty na wiele formatów tekstowych. Takie narzÚdzie mogïoby przeprowadzaÊ konwersjÚ dokumentów RTF na zwykïy tekst w formacie ASCII lub na widget tekstowy, który moĝna interaktywnie edytowaÊ. Jednak problem polega na tym, ĝe liczba moĝliwych prze- ksztaïceñ jest nieokreĂlona. Dlatego naleĝy zachowaÊ moĝliwoĂÊ ïatwego dodawania nowych metod konwersji bez koniecznoĂci modyfikowania czytnika. RozwiÈzanie polega na skonfigurowaniu klasy RTFReader za pomocÈ obiektu TextConverter przeksztaïcajÈcego dokumenty RTF na innÈ reprezentacjÚ tekstowÈ. Klasa RTFReader w czasie analizowania dokumentu RTF korzysta z obiektu TextConverter do przeprowadzania kon- wersji. Kiedy klasa RTFReader wykryje znacznik formatu RTF (w postaci zwykïego tekstu lub sïowa sterujÈcego z tego formatu), przekaĝe do obiektu TextConverter ĝÈdanie przeksztaïce- nia znacznika. Obiekty TextConverter odpowiadajÈ zarówno za przeprowadzanie konwersji danych, jak i zapisywanie znacznika w okreĂlonym formacie. Podklasy klasy TextConverter sÈ wyspecjalizowane pod kÈtem róĝnych konwersji i formatów. Na przykïad klasa ASCIIConverter ignoruje ĝÈdania zwiÈzane z konwersjÈ elementów in- nych niĝ zwykïy tekst. Z kolei klasa TeXConverter obejmuje implementacjÚ operacji obsïugu- jÈcych wszystkie ĝÈdania, co umoĝliwia utworzenie reprezentacji w formacie TEX, uwzglÚd- niajÈcej wszystkie informacje na temat stylu tekstu. Klasa TextWidgetConverter generuje zïoĝony obiekt interfejsu uĝytkownika umoĝliwiajÈcy oglÈdanie i edytowanie tekstu. Kup książkęPoleć książkę BUDOWNICZY (BUILDER) 993 Kaĝda klasa konwertujÈca przyjmuje mechanizm tworzenia i skïadania obiektów zïoĝonych oraz ukrywa go za abstrakcyjnym interfejsem. Konwerter jest oddzielony od czytnika odpo- wiadajÈcego za analizowanie dokumentów RTF. Wzorzec Budowniczy ujmuje wszystkie te relacje. W tym wzorcu kaĝda klasa konwertujÈca nosi nazwÚ builder (czyli budowniczy), a klasa czytnika to director (czyli kierownik). Zasto- sowanie wzorca Budowniczy w przytoczonym przykïadzie powoduje oddzielenie algorytmu interpretujÈcego format tekstowy (czyli parsera dokumentów RTF) od procesu tworzenia i re- prezentowania przeksztaïconego dokumentu. Umoĝliwia to powtórne wykorzystanie algo- rytmu analizujÈcego z klasy RTFReader do przygotowania innych reprezentacji tekstu z doku- mentów RTF. Aby to osiÈgnÈÊ, wystarczy skonfigurowaÊ klasÚ RTFReader za pomocÈ innej podklasy klasy TextConverter. WARUNKI STOSOWANIA Wzorca Budowniczy naleĝy uĝywaÊ w nastÚpujÈcych sytuacjach: Ź JeĂli algorytm tworzenia obiektu zïoĝonego powinien byÊ niezaleĝny od skïadników tego obiektu i sposobu ich ïÈczenia. Ź Kiedy proces konstrukcji musi umoĝliwiaÊ tworzenie róĝnych reprezentacji generowanego obiektu. STRUKTURA ELEMENTY Ź Builder (TextConverter), czyli budowniczy: – okreĂla interfejs abstrakcyjny do tworzenia skïadników obiektu Product. Ź ConcreteBuilder (ASCIIConverter, TeXConverter, TextWidgetConverter), czyli bu- tworzy i ïÈczy skïadniki produktu w implementacji interfejsu klasy Builder; downiczy konkretny: – – definiuje i Ăledzi generowane reprezentacje; – udostÚpnia interfejs do pobierania produktów (na przykïad operacje GetASCIIText i GetTextWidget). Kup książkęPoleć książkę 94 Rozdziaï 3. • WZORCE KONSTRUKCYJNE Ź Director (RTFReader), czyli kierownik: – tworzy obiekt za pomocÈ interfejsu klasy Builder. Ź Product (ASCIIText, TeXText, TextWidget): – reprezentuje generowany obiekt zïoĝony; klasa ConcreteBuilder tworzy wewnÚtrznÈ reprezentacjÚ produktu i definiuje proces jej skïadania; – obejmuje klasy definiujÈce skïadowe elementy obiektu, w tym interfejsy do ïÈczenia skïadowych w ostatecznÈ postaÊ obiektu. WSPӒDZIA’ANIE Ź Klient tworzy obiekt Director i konfiguruje go za pomocÈ odpowiedniego obiektu Builder. Ź Kiedy potrzebne jest utworzenie czÚĂci produktu, obiekt Director wysyïa powiadomienie do obiektu Builder. Ź Obiekt Builder obsïuguje ĝÈdania od obiektu Director i dodaje czÚĂci do produktu. Ź Klient pobiera produkt od obiektu Builder. Poniĝszy diagram interakcji pokazuje, w jaki sposób klasy Builder i Director wspóïdziaïajÈ z klientem. KONSEKWENCJE Oto kluczowe konsekwencje zastosowania wzorca Budowniczy: 1. MoĝliwoĂÊ modyfikowania wewnÚtrznej reprezentacji produktu. Obiekt Builder udostÚpnia obiektowi Director interfejs abstrakcyjny do tworzenia produktu. Interfejs ten umoĝliwia obiektowi Builder ukrycie reprezentacji i wewnÚtrznej struktury produktu, a takĝe sposobu jego skïadania. Poniewaĝ do tworzenia produktu sïuĝy interfejs abstrakcyjny, zmiana wewnÚtrznej reprezentacji produktu wymaga jedynie zdefiniowania obiektu Builder nowego rodzaju. Kup książkęPoleć książkę BUDOWNICZY (BUILDER) 995 2. Odizolowanie reprezentacji od kodu sïuĝÈcego do tworzenia produktu. Wzorzec Budowniczy po- maga zwiÚkszyÊ modularnoĂÊ, poniewaĝ kapsuïkuje sposób tworzenia i reprezentowania obiektu zïoĝonego. Klienty nie potrzebujÈ ĝadnych informacji o klasach definiujÈcych wewnÚtrznÈ strukturÚ produktu, poniewaĝ klasy te nie wystÚpujÈ w interfejsie obiektu Builder. Kaĝdy obiekt ConcreteBuilder obejmuje caïy kod potrzebny do tworzenia i skïadania produktów okreĂlonego rodzaju. Kod ten wystarczy napisaÊ raz. NastÚpnie moĝna wielo- krotnie wykorzystaÊ go w róĝnych obiektach Director do utworzenia wielu odmian obiektu Product za pomocÈ tych samych skïadników. W przykïadzie dotyczÈcym dokumentów RTF moglibyĂmy zdefiniowaÊ czytnik dokumentów o formacie innym niĝ RTF, na przykïad klasÚ SGMLReader, i uĝyÊ tych samych podklas klasy TextConverter do wygenerowania repre- zentacji dokumentów SGML w postaci obiektów ASCIIText, TeXText i TextWidget. 3. WiÚksza kontrola nad procesem tworzenia. Wzorzec Budowniczy — w odróĝnieniu od wzorców konstrukcyjnych tworzÈcych produkty w jednym etapie — polega na generowaniu ich krok po kroku pod kontrolÈ obiektu Director. Dopiero po ukoñczeniu produktu obiekt Director odbiera go od obiektu Builder. Dlatego interfejs klasy Builder w wiÚkszym stopniu niĝ inne wzorce konstrukcyjne odzwierciedla proces tworzenia produktów. Zapewnia to peïniejszÈ kontrolÚ nad tym procesem, a tym samym i wewnÚtrznÈ strukturÈ gotowego produktu. IMPLEMENTACJA Zwykle w implementacji znajduje siÚ klasa abstrakcyjna Builder obejmujÈca definicjÚ operacji dla kaĝdego komponentu, którego utworzenia moĝe zaĝÈdaÊ obiekt Director. DomyĂlnie operacje te nie wykonujÈ ĝadnych dziaïañ. W klasie ConcreteBuilder przesïoniÚte sÈ operacje komponentów, które klasa ta ma generowaÊ. Oto inne zwiÈzane z implementacjÈ kwestie, które naleĝy rozwaĝyÊ: 1. Interfejs do skïadania i tworzenia obiektów. Obiekty Builder tworzÈ produkty krok po kroku. Dlatego interfejs klasy Builder musi byÊ wystarczajÈco ogólny, aby umoĝliwiaï konstru- owanie produktów kaĝdego rodzaju przez konkretne podklasy klasy Builder. Kluczowa kwestia projektowa dotyczy modelu procesu tworzenia i skïadania obiektów. Zwykle wystarczajÈcy jest model, w którym efekty zgïoszenia ĝÈdania konstrukcji sÈ po prostu doïÈczane do produktu. W przykïadzie zwiÈzanym z dokumentami RTF obiekt Builder przeksztaïca i doïÈcza nastÚpny znacznik do wczeĂniej skonwertowanego tekstu. Jednak czasem potrzebny jest dostÚp do wczeĂniej utworzonych czÚĂci produktu. W przy- kïadzie dotyczÈcym labiryntów, który prezentujemy w punkcie Przykïadowy kod, interfejs klasy MazeBuilder umoĝliwia dodanie drzwi miÚdzy istniejÈcymi pomieszczeniami. NastÚpnym przykïadem, w którym jest to potrzebne, sÈ budowane od doïu do góry struktury drzewiaste, takie jak drzewa skïadni. Wtedy obiekt Builder zwraca wÚzïy podrzÚdne obiektowi Director, który nastÚpnie przekazuje je ponownie do obiektu Builder, aby ten utworzyï wÚzïy nadrzÚdne. Kup książkęPoleć książkę 96 Rozdziaï 3. • WZORCE KONSTRUKCYJNE 2. Dlaczego nie istnieje klasa abstrakcyjna produktów? W typowych warunkach produkty tworzone przez obiekty ConcreteBuilder majÈ tak odmiennÈ reprezentacjÚ, ĝe udostÚpnienie wspól- nej klasy nadrzÚdnej dla róĝnych produktów przynosi niewielkie korzyĂci. W przykïadzie dotyczÈcym dokumentów RTF obiekty ASCIIText i TextWidget prawdopodobnie nie bÚdÈ miaïy wspólnego interfejsu ani teĝ go nie potrzebujÈ. Poniewaĝ klienty zwykle konfigurujÈ obiekt Director za pomocÈ odpowiedniego obiektu ConcreteBuilder, klient potrafi okre- ĂliÊ, która podklasa konkretna klasy Builder jest uĝywana, i na tej podstawie obsïuguje dostÚpne produkty. 3. Zastosowanie pustych metod domyĂlnych w klasie Builder. W jÚzyku C++ metody sïuĝÈce do tworzenia obiektów celowo nie sÈ deklarowane jako czysto wirtualne funkcje skïadowe. W zamian definiuje siÚ je jako puste metody, dziÚki czemu w klientach trzeba przesïoniÊ tylko potrzebne operacje. PRZYK’ADOWY KOD Zdefiniujmy nowÈ wersjÚ funkcji skïadowej CreateMaze (s. 90). BÚdzie ona przyjmowaÊ jako argument obiekt budujÈcy klasy MazeBuilder. Klasa MazeBuilder definiuje poniĝszy interfejs sïuĝÈcy do tworzenia labiryntów: class MazeBuilder { public: virtual void BuildMaze() { } virtual void BuildRoom(int room) { } virtual void BuildDoor(int roomFrom, int roomTo) { } virtual Maze* GetMaze() { return 0; } protected: MazeBuilder(); }; Ten interfejs pozwala utworzyÊ trzy elementy: (1) labirynt, (2) pomieszczenia o okreĂlonym numerze i (3) drzwi miÚdzy ponumerowanymi pokojami. Operacja GetMaze zwraca labirynt klientowi. W podklasach klasy MazeBuilder naleĝy jÈ przesïoniÊ, aby zwracaïy one genero- wany przez siebie labirynt. Wszystkie zwiÈzane z budowaniem labiryntu operacje klasy MazeBuilder domyĂlnie nie wy- konujÈ ĝadnych dziaïañ. Jednak nie sÈ zadeklarowane jako czysto wirtualne, dziÚki czemu w klasach pochodnych wystarczy przesïoniÊ tylko potrzebne metody. Po utworzeniu interfejsu klasy MazeBuilder moĝna zmodyfikowaÊ funkcjÚ skïadowÈ CreateMaze, aby przyjmowaïa jako parametr obiekt tej klasy: Maze* MazeGame::CreateMaze (MazeBuilder builder) { builder.BuildMaze(); builder.BuildRoom(1); builder.BuildRoom(2); builder.BuildDoor(1, 2); return builder.GetMaze(); } Kup książkęPoleć książkę BUDOWNICZY (BUILDER) 997 Porównajmy tÚ wersjÚ operacji CreateMaze z jej pierwowzorem. Warto zauwaĝyÊ, w jaki spo- sób w budowniczym ukryto wewnÚtrznÈ reprezentacjÚ labiryntu — czyli klasy z definicjami pomieszczeñ, drzwi i Ăcian — i jak elementy te sÈ skïadane w gotowy labirynt. Moĝna siÚ do- myĂliÊ, ĝe istniejÈ klasy reprezentujÈce pomieszczenia i drzwi, jednak w kodzie nie ma wska- zówek dotyczÈcych klasy zwiÈzanej ze Ăcianami. Uïatwia to zmianÚ reprezentacji labiryntu, poniewaĝ nie trzeba modyfikowaÊ kodu ĝadnego z klientów uĝywajÈcych klasy MazeBuilder. Wzorzec Budowniczy — podobnie jak inne wzorce konstrukcyjne — kapsuïkuje tworzenie obiektów. Tutaj sïuĝy do tego interfejs zdefiniowany w klasie MazeBuilder. Oznacza to, ĝe moĝemy wielokrotnie wykorzystaÊ tÚ klasÚ do tworzenia labiryntów róĝnego rodzaju. Przy- kïadem na to jest operacja CreateComplexMaze: Maze* MazeGame::CreateComplexMaze (MazeBuilder builder) { builder.BuildRoom(1); // ... builder.BuildRoom(1001); return builder.GetMaze(); } Warto zauwaĝyÊ, ĝe klasa MazeBuilder nie tworzy labiryntu. Sïuĝy ona gïównie do definio- wania interfejsu do generowania labiryntów. Puste implementacje znajdujÈ siÚ w niej dla wy- gody programisty, natomiast potrzebne dziaïania wykonujÈ podklasy klasy MazeBuilder. Podklasa StandardMazeBuilder to implementacja sïuĝÈca do tworzenia prostych labiryntów. Zapisuje ona budowany labirynt w zmiennej _currentMaze. class StandardMazeBuilder : public MazeBuilder { public: StandardMazeBuilder(); virtual void BuildMaze(); virtual void BuildRoom(int); virtual void BuildDoor(int, int); virtual Maze* GetMaze(); private: Direction CommonWall(Room*, Room*); Maze* _currentMaze; }; CommonWall to operacja narzÚdziowa okreĂlajÈca kierunek standardowej Ăciany pomiÚdzy dwoma pomieszczeniami. Konstruktor StandardMazeBuilder po prostu inicjuje zmiennÈ _currentMaze. StandardMazeBuilder::StandardMazeBuilder () { _currentMaze = 0; } Operacja BuildMaze tworzy egzemplarz klasy Maze, który pozostaïe operacje skïadajÈ i osta- tecznie zwracajÈ do klienta (za to odpowiada operacja GetMaze). Kup książkęPoleć książkę 98 Rozdziaï 3. • WZORCE KONSTRUKCYJNE void StandardMazeBuilder::BuildMaze () { _currentMaze = new Maze; } Maze* StandardMazeBuilder::GetMaze () { return _currentMaze; } Operacja BuildRoom tworzy pomieszczenie i Ăciany wokóï niego. void StandardMazeBuilder::BuildRoom (int n) { if (!_currentMaze- RoomNo(n)) { Room* room = new Room(n); _currentMaze- AddRoom(room); room- SetSide(North, new Wall); room- SetSide(South, new Wall); room- SetSide(East, new Wall); room- SetSide(West, new Wall); } } Aby utworzyÊ drzwi miÚdzy dwoma pomieszczeniami, obiekt StandardMazeBuilder wyszu- kuje w labiryncie odpowiednie pokoje i ïÈczÈcÈ je ĂcianÚ. void StandardMazeBuilder::BuildDoor (int n1, int n2) { Room* r1 = _currentMaze- RoomNo(n1); Room* r2 = _currentMaze- RoomNo(n2); Door* d = new Door(r1, r2); r1- SetSide(CommonWall(r1,r2), d); r2- SetSide(CommonWall(r2,r1), d); } Klienty mogÈ teraz uĝyÊ do utworzenia labiryntu operacji CreateMaze wraz z obiektem StandardMazeBuilder. Maze* maze; MazeGame game; StandardMazeBuilder builder; game.CreateMaze(builder); maze = builder.GetMaze(); MoglibyĂmy umieĂciÊ wszystkie operacje klasy StandardMazeBuilder w klasie Maze i pozwoliÊ kaĝdemu obiektowi Maze, aby samodzielnie utworzyï swój egzemplarz. Jednak zmniej- szenie klasy Maze sprawia, ĝe ïatwiej bÚdzie jÈ zrozumieÊ i zmodyfikowaÊ, a wyodrÚbnienie z niej klasy StandardMazeBuilder nie jest trudne. Co jednak najwaĝniejsze, rozdzielenie tych klas pozwala utworzyÊ róĝnorodne obiekty z rodziny MazeBuilder, z których kaĝdy uĝywa innych klas do generowania pomieszczeñ, Ăcian i drzwi. Kup książkęPoleć książkę CountingMazeBuilder to bardziej wymyĂlna podklasa klasy MazeBuilder. Budowniczowie tego typu w ogóle nie tworzÈ labiryntów, a jedynie zliczajÈ utworzone komponenty róĝnych rodzajów. BUDOWNICZY (BUILDER) 999 class CountingMazeBuilder : public MazeBuilder { public: CountingMazeBuilder(); virtual void BuildMaze(); virtual void BuildRoom(int); virtual void BuildDoor(int, int); virtual void AddWall(int, Direction); void GetCounts(int , int ) const; private: int _doors; int _rooms; }; Konstruktor inicjuje liczniki, a przesïoniÚte operacje klasy MazeBuilder w odpowiedni sposób powiÚkszajÈ ich wartoĂÊ. CountingMazeBuilder::CountingMazeBuilder () { _rooms = _doors = 0; } void CountingMazeBuilder::BuildRoom (int) { _rooms++; } void CountingMazeBuilder::BuildDoor (int, int) { _doors++; } void CountingMazeBuilder::GetCounts ( int rooms, int doors ) const { rooms = _rooms; doors = _doors; } Klient moĝe korzystaÊ z klasy CountingMazeBuilder w nastÚpujÈcy sposób: int rooms, doors; MazeGame game; CountingMazeBuilder builder; game.CreateMaze(builder); builder.GetCounts(rooms, doors); cout Liczba pomieszczeñ w labiryncie to rooms , a liczba drzwi wynosi doors . « endl; Kup książkęPoleć książkę 100 Rozdziaï 3. • WZORCE KONSTRUKCYJNE ZNANE ZASTOSOWANIA Aplikacja do konwersji dokumentów RTF pochodzi z platformy ET++ [WGM88]. Jej czÚĂÊ sïuĝÈca do obsïugi tekstu wykorzystuje budowniczego do przetwarzania tekstu zapisanego w formacie RTF. Wzorzec Budowniczy jest czÚsto stosowany w jÚzyku Smalltalk-80 [Par90]: Ź Klasa Parser w podsystemie odpowiedzialnym za kompilacjÚ peïni funkcjÚ kierownika i przyjmuje jako argument obiekt ProgramNodeBuilder. Obiekt Parser za kaĝdym razem, kiedy rozpozna danÈ konstrukcjÚ skïadniowÈ, wysyïa do powiÈzanego z nim obiektu ProgramNodeBuilder powiadomienie. Kiedy parser koñczy dziaïanie, ĝÈda od budowniczego utworzenia drzewa skïadni i przekazuje je klientowi. Ź ClassBuilder to budowniczy, którego klasy uĝywajÈ do tworzenia swoich podklas. W tym przypadku klasa jest zarówno kierownikiem, jak i produktem. Ź ByteCodeStream to budowniczy, który tworzy skompilowanÈ metodÚ w postaci tablicy bajtów. Klasa ByteCodeStream to przykïad niestandardowego zastosowania wzorca Budowniczy, poniewaĝ generowany przez niÈ obiekt zïoĝony jest kodowany jako tablica bajtów, a nie jako zwykïy obiekt jÚzyka Smalltalk. Jednak interfejs klasy ByteCodeStream jest typowy dla budowniczych i ïatwo moĝna zastÈpiÊ tÚ klasÈ innÈ, reprezentujÈcÈ programy jako obiekty skïadowe. Platforma Service Configurator wchodzÈca w skïad Ărodowiska Adaptive Communications Environment korzysta z budowniczych do tworzenia komponentów usïug sieciowych doïÈ- czanych do serwera w czasie jego dziaïania [SS94]. Komponenty te sÈ opisane w jÚzyku konfi- guracyjnym analizowanym przez parser LALR(1). Akcje semantyczne parsera powodujÈ wy- konanie operacji na budowniczym, który dodaje informacje do komponentu usïugowego. W tym przykïadzie parser peïni funkcjÚ kierownika. POWIkZANE WZORCE Fabryka abstrakcyjna (s. 101) przypomina wzorzec Budowniczy, poniewaĝ teĝ moĝe sïuĝyÊ do tworzenia obiektów zïoĝonych. Gïówna róĝnica miÚdzy nimi polega na tym, ĝe wzorzec Budowniczy opisuje przede wszystkim tworzenie obiektów zïoĝonych krok po kroku. We wzorcu Fabryka abstrakcyjna nacisk poïoĝony jest na rodziny obiektów-produktów (zarówno prostych, jak i zïoĝonych). Budowniczy zwraca produkt w ostatnim kroku, natomiast we wzorcu Fabryka abstrakcyjna produkt jest udostÚpniany natychmiast. Budowniczy czÚsto sïuĝy do tworzenia kompozytów (s. 170). Kup książkęPoleć książkę FABRYKA ABSTRAKCYJNA (ABSTRACT FACTORY) 1101 FABRYKA ABSTRAKCYJNA (ABSTRACT FACTORY) obiektowy, konstrukcyjny PRZEZNACZENIE UdostÚpnia interfejs do tworzenia rodzin powiÈzanych ze sobÈ lub zaleĝnych od siebie obiektów bez okreĂlania ich klas konkretnych. INNE NAZWY Zestaw (ang. kit). UZASADNIENIE Zastanówmy siÚ nad pakietem narzÚdziowym do tworzenia interfejsów uĝytkownika, obsïu- gujÈcym róĝne standardy wyglÈdu i dziaïania (na przykïad Motif i Presentation Manager). Poszczególne standardy wyznaczajÈ róĝny wyglÈd i inne zachowanie widgetów interfejsu uĝytkownika, takich jak paski przewijania, okna i przyciski. Aby aplikacja byïa przenoĂna miÚdzy róĝnymi standardami, nie naleĝy zapisywaÊ w niej na staïe okreĂlonego wyglÈdu i sposobu dziaïania widgetów. Tworzenie okreĂlajÈcych te aspekty egzemplarzy klas w róĝnych miejscach aplikacji utrudnia póěniejszÈ zmianÚ jej wyglÈdu i zachowania. Moĝemy rozwiÈzaÊ ten problem przez zdefiniowanie klasy abstrakcyjnej WidgetFactory i zade- klarowanie w niej interfejsu do tworzenia podstawowych widgetów. Naleĝy przygotowaÊ teĝ klasy abstrakcyjne dla poszczególnych rodzajów widgetów oraz podklasy konkretne z implementacjÈ okreĂlonych standardów wyglÈdu i dziaïania. Interfejs klasy WidgetFactory obejmuje operacje, które zwracajÈ nowe obiekty dla klas abstrakcyjnych reprezentujÈcych poszczególne widgety. Klienty wywoïujÈ te operacje, aby otrzymaÊ egzemplarze widgetów, ale nie wiedzÈ, której klasy konkret- nej uĝywajÈ. Dlatego klienty pozostajÈ niezaleĝne od stosowanego standardu wyglÈdu i dziaïania. Kup książkęPoleć książkę 102 Rozdziaï 3. • WZORCE KONSTRUKCYJNE Dla kaĝdego standardu wyglÈdu i dziaïania istnieje podklasa konkretna klasy WidgetFactory. W kaĝdej takiej podklasie zaimplementowane sÈ operacje do tworzenia widgetów odpowiednich dla danego standardu. Na przykïad operacja CreateScrollBar klasy MotifWidgetFactory tworzy i zwraca egzemplarz paska przewijania zgodnego ze standardem Motif, natomiast odpowiadajÈca jej operacja klasy PMWidgetFactory tworzy pasek przewijania dla standardu Presentation Manager. Klienty tworzÈ widgety wyïÈcznie za poĂrednictwem interfejsu klasy WidgetFactory i nie znajÈ klas z implementacjami widgetów dla okreĂlonych standardów wyglÈdu i dziaïania. Oznacza to, ĝe klienty muszÈ byÊ zgodne tylko z interfejsem klasy abs- trakcyjnej, a nie z konkretnymi klasami konkretnymi. Klasa WidgetFactory wymusza ponadto zaleĝnoĂci miÚdzy klasami konkretnymi widgetów. Paska przewijania standardu Motif naleĝy uĝywaÊ wraz z przyciskiem i edytorem tekstu zgodnymi z tym standardem. Ograniczenie to jest wymuszane automatycznie (jest to skutek zastosowania klasy MotifWidgetFactory). WARUNKI STOSOWANIA Wzorzec Fabryka abstrakcyjna naleĝy stosowaÊ w nastÚpujÈcych warunkach: Ź Kiedy system powinien byÊ niezaleĝny od sposobu tworzenia, skïadania i reprezentowania jego produktów. Ź JeĂli system naleĝy skonfigurowaÊ za pomocÈ jednej z wielu rodzin produktów. Ź Jeĝeli powiÈzane obiekty-produkty z jednej rodziny sÈ zaprojektowane do wspólnego uĝytku i trzeba wymusiÊ jednoczesne korzystanie z tych obiektów. Ź Kiedy programista chce udostÚpniÊ klasÚ bibliotecznÈ produktów i ujawniÊ jedynie ich interfejsy, a nie implementacje. STRUKTURA Kup książkęPoleć książkę FABRYKA ABSTRAKCYJNA (ABSTRACT FACTORY) 1103 ELEMENTY Ź AbstractFactory (WidgetFactory), czyli fabryka abstrakcyjna: – obejmuje deklaracjÚ interfejsu z operacjami tworzÈcymi produkty abstrakcyjne. Ź ConcreteFactory (MotifWidgetFactory, PMWidgetFactory), czyli fabryka konkretna: – obejmuje implementacjÚ operacji tworzÈcych produkty konkretne. Ź AbstractProduct (Window, ScrollBar), czyli produkt abstrakcyjny: – obejmuje deklaracjÚ interfejs dla produktów okreĂlonego typu. Ź ConcreteProduct (MotifWindow, MotifScrollBar), czyli produkt konkretny: – definiuje obiekt-produkt tworzony przez odpowiadajÈcÈ mu fabrykÚ konkretnÈ; – obejmuje implementacjÚ interfejsu klasy AbstractProduct. Ź Client: – korzysta jedynie z interfejsów zadeklarowanych w klasach AbstractFactory i Abstract ´Product. WSPӒDZIA’ANIE Ź W czasie wykonywania programu powstaje zwykle jeden egzemplarz klasy ConcreteFactory. Ta fabryka konkretna tworzy obiekty-produkty o okreĂlonej implementacji. Aby wygene- rowaÊ róĝne obiekty-produkty, klienty muszÈ uĝyÊ odmiennych fabryk konkretnych. Ź Klasa AbstractFactory przekazuje tworzenie obiektów-produktów do swojej podklasy ConcreteFactory. KONSEKWENCJE Wzorzec Fabryka abstrakcyjna ma nastÚpujÈce zalety i wady: 1. Izoluje klasy konkretne. Wzorzec Fabryka abstrakcyjna pomaga kontrolowaÊ klasy obiektów tworzonych przez aplikacjÚ. Poniewaĝ fabryka kapsuïkuje zadanie i proces tworzenia obiektów-produktów, izoluje klienty od klas zawierajÈcych implementacjÚ. Klienty mani- pulujÈ egzemplarzami tych klas za poĂrednictwem interfejsów abstrakcyjnych. Nazwy klas produktów sÈ odizolowane w implementacji fabryki konkretnej i nie pojawiajÈ siÚ w kodzie klienckim. 2. Uïatwia zastÚpowanie rodzin produktów. Klasa fabryki konkretnej pojawia siÚ w aplikacji tylko raz — w miejscu tworzenia jej egzemplarza. Dlatego ïatwo jest zmieniÊ fabrykÚ konkretnÈ wykorzystywanÈ przez aplikacjÚ. Aby uĝyÊ w programie innego zestawu produktów, wy- starczy podaÊ innÈ fabrykÚ konkretnÈ. Poniewaĝ fabryka abstrakcyjna tworzy kompletnÈ rodzinÚ produktów, jednoczeĂnie zmieniana jest caïa taka rodzina. W przykïadowym interfejsie uĝytkownika moĝna zastÈpiÊ widgety standardu Motif widgetami standardu Presentation Manager w prosty sposób — przez podmianÚ odpowiednich obiektów-fabryk i odtworzenie interfejsu. Kup książkęPoleć książkę 104 Rozdziaï 3. • WZORCE KONSTRUKCYJNE 3. Uïatwia zachowanie spójnoĂci miÚdzy produktami. JeĂli obiekty-produkty z danej rodziny sÈ zaprojektowane tak, aby uĝywaÊ ich razem, waĝne jest, aby aplikacja w danym momencie korzystaïa z obiektów z tylko jednej rodziny. Klasa AbstractFactory pozwala w ïatwy sposób wymusiÊ to ograniczenie. 4. Utrudnia dodawanie obsïugi produktów nowego rodzaju. Rozszerzanie fabryk abstrakcyjnych w celu tworzenia produktów nowego typu nie jest proste. Wynika to z tego, ĝe w interfejsie klasy AbstractFactory na staïe zapisany jest zestaw produktów, które moĝna utworzyÊ. Aby dodaÊ obsïugÚ produktów nowego rodzaju, trzeba rozszerzyÊ interfejs fabryki, co wymaga zmodyfikowania klasy AbstractFactory i wszystkich jej podklas. Jedno z roz- wiÈzañ tego problemu omawiamy w punkcie Implementacja. IMPLEMENTACJA Oto kilka technik przydatnych przy implementowaniu wzorca Fabryka abstrakcyjna. 1. Fabryki jako singletony. W aplikacji zwykle potrzebny jest tylko jeden egzemplarz klasy ConcreteFactory na kaĝdÈ rodzinÚ produktów. Dlatego zazwyczaj najlepiej jest imple- mentowaÊ takie klasy zgodnie ze wzorcem Singleton (s. 130). 2. Tworzenie produktów. Klasa AbstractFactory obejmuje jedynie deklaracjÚ interfejsu do two- rzenia produktów. To podklasy ConcreteProduct odpowiadajÈ za ich generowanie. Naj- czÚĂciej definiowana jest w tym celu metoda wytwórcza (zobacz wzorzec Metoda wytwórcza, s. 110) dla kaĝdego produktu. W fabryce konkretnej generowane produkty sÈ okreĂlane przez przesïoniÚcie metody fabrycznej dla kaĝdego z tych produktów. ChoÊ taka imple- mentacja jest prosta, wymaga przygotowania dla kaĝdej rodziny produktów nowej pod- klasy konkretnej reprezentujÈcej fabrykÚ, nawet jeĂli róĝnice miÚdzy poszczególnymi rodzinami sÈ niewielkie. JeĂli aplikacja moĝe obejmowaÊ wiele rodzin produktów, fabrykÚ konkretnÈ moĝna zaim- plementowaÊ za pomocÈ wzorca Prototyp (s. 120). Fabryka konkretna jest wtedy inicjowana za pomocÈ prototypowego egzemplarza kaĝdego produktu z rodziny i tworzy nowe pro- dukty przez klonowanie ich prototypów. PodejĂcie oparte na wzorcu Prototyp pozwala wyeliminowaÊ koniecznoĂÊ tworzenia dla kaĝdej rodziny produktów nowej klasy kon- kretnej reprezentujÈcej fabrykÚ. Oto sposób na zaimplementowanie fabryki opartej na wzorcu Prototyp w jÚzyku Smalltalk. Fabryka konkretna przechowuje klonowane prototypy w sïowniku o nazwie partCatalog. Metoda make: pobiera prototyp i klonuje go: make: partName ^ (partCatalog at: partName) copy Fabryka konkretna obejmuje metodÚ do dodawania elementów do katalogu: addPart: partTemplate named: partName partCatalog at: partName put: partTemplate Prototypy sÈ dodawane do fabryki przez wskazanie ich za pomocÈ symbolu: aFactory addPart: aPrototype named: #ACMEWidget Kup książkęPoleć książkę FABRYKA ABSTRAKCYJNA (ABSTRACT FACTORY) 1105 W jÚzykach, w których klasy sÈ traktowane jak standardowe obiekty (na przykïad w jÚzy- kach Smalltalk i Objective C), moĝna zastosowaÊ pewnÈ odmianÚ podejĂcia opartego na wzorcu Prototyp. W tych jÚzykach klasy moĝna uznaÊ za uproszczone fabryki tworzÈce produkty tylko jednego rodzaju. W tworzÈcej produkty fabryce konkretnej moĝna przypisaÊ do zmiennych klasy (podobnie jak prototypy). Te klasy bÚdÈ tworzyÊ nowe egzemplarze na rzecz fabryki konkretnej. Aby zdefiniowaÊ nowÈ fabrykÚ, naleĝy zainicjowaÊ egzemplarz fabryki konkretnej za pomocÈ klas produktów, zamiast tworzyÊ podklasÚ. To podejĂcie po- zwala wykorzystaÊ specyficzne cechy jÚzyków, natomiast podstawowe rozwiÈzanie oparte na wzorcu Prototyp jest niezaleĝne od jÚzyka. Wersja oparta na klasach — podobnie jak opisane wïaĂnie fabryki oparte na wzorcu Prototyp napisane w jÚzyku Smalltalk — ma jednÈ zmiennÈ egzemplarza (partCatalog). Jest to sïownik, którego kluczami sÈ nazwy poszczególnych elementów. Zmienna partCatalog nie przechowuje przeznaczonych do sklonowania prototypów, ale klasy produktów. Nowa wersja metody make: wyglÈda tak: make: partName ^ (partCatalog at: partName) new 3. Definiowanie rozszerzalnych fabryk. W klasie AbstractFactory zwykle zdefiniowane sÈ róĝne operacje dla wszystkich rodzajów produktów generowanych przez tÚ klasÚ. Rodzaje pro- duktów sÈ okreĂlone w sygnaturach operacji. Dodanie produktu nowego rodzaju wymaga zmodyfikowania interfejsu klasy AbstractFactory i wszystkich klas od niego zaleĝnych. Elastyczniejszy (choÊ mniej bezpieczny) projekt wymaga dodania parametru do operacji tworzÈcych obiekty. Ten parametr okreĂla rodzaj generowanego obiektu. Jako parametru moĝna uĝyÊ identyfikatora klasy, liczby caïkowitej, ïañcucha znaków lub dowolnego innego elementu identyfikujÈcego rodzaj produktu. W tym podejĂciu klasa AbstractFactory po- trzebuje jedynie pojedynczej operacji Make z parametrem okreĂlajÈcym rodzaj tworzonego obiektu. Tej techniki uĝyliĂmy w omówionych wczeĂniej fabrykach abstrakcyjnych opartych na wzorcu Prototyp lub klasie. TÚ wersjÚ ïatwiej jest stosowaÊ w jÚzykach z dynamicznÈ kontrolÈ typów (takich jak Smalltalk) niĝ w jÚzykach ze statycznÈ kontrolÈ typów (na przykïad C++). W jÚzyku C++ rozwiÈzania tego moĝna uĝyÊ tylko wtedy, jeĂli wszystkie obiekty majÈ tÚ samÈ abstrakcyj- nÈ klasÚ bazowÈ lub gdy klient, który zaĝÈdaï produktów, moĝe bezpiecznie przeksztaïciÊ ich typ na wïaĂciwy. W punkcie Implementacja poĂwiÚconym wzorcowi Metoda wytwórcza (s. 110) pokazujemy, jak zaimplementowaÊ takie sparametryzowane operacje w jÚzyku C++. Jednak nawet kiedy przeksztaïcanie na wïaĂciwy typ nie jest konieczne, pozostaje do roz- wiÈzania pewien problem — wszystkie produkty przekazywane do klienta majÈ ten sam abstrakcyjny interfejs okreĂlony przez zwracany typ. Dlatego klient nie moĝe rozróĝniÊ klas produktów ani dokonywaÊ bezpiecznych zaïoĝeñ na ich temat. JeĂli klient musi wy- konaÊ operacje specyficzne dla podklasy, nie bÚdzie mógï uzyskaÊ dostÚpu do nich za po- Ărednictwem abstrakcyjnego interfejsu. ChoÊ klient moĝe przeprowadziÊ rzutowanie w dóï (na przykïad za pomocÈ instrukcji dynamic_cast w jÚzyku C++), nie zawsze jest to wy- konalne lub bezpieczne, poniewaĝ operacja ta moĝe zakoñczyÊ siÚ niepowodzeniem. Jest to typowy koszt utworzenia wysoce elastycznego i rozszerzalnego interfejsu. Kup książkęPoleć książkę 106 Rozdziaï 3. • WZORCE KONSTRUKCYJNE PRZYK’ADOWY KOD Zastosujmy wzorzec Fabryka abstrakcyjna do utworzenia labiryntów opisanych w poczÈtkowej czÚĂci rozdziaïu. Klasa MazeFactory sïuĝy do tworzenia elementów labiryntów — pomieszczeñ, Ăcian i drzwi miÚdzy pokojami. Moĝna uĝyÊ jej w programie, który wczytuje plany labiryntów z pliku i two- rzy odpowiednie labirynty. Ponadto moĝna wykorzystaÊ jÈ w aplikacji generujÈcej labirynty w sposób losowy. Programy, które tworzÈ labirynty, przyjmujÈ obiekt MazeFactory jako ar- gument, dziÚki czemu programista moĝe okreĂliÊ generowane pomieszczenia, Ăciany i drzwi. class MazeFactory { public: MazeFactory(); virtual Maze* MakeMaze() const { return new Maze; } virtual Wall* MakeWall() const { return new Wall; } virtual Room* MakeRoom(int n) const { return new Room(n); } virtual Door* MakeDoor(Room* r1, Room* r2) const { return new Door(r1, r2); } }; Przypomnijmy, ĝe funkcja skïadowa CreateMaze (s. 90) tworzy maïy labirynt skïadajÈcy siÚ z dwóch pomieszczeñ i drzwi miÚdzy nimi. W tej funkcji nazwy klas zapisane sÈ na staïe, co utrudnia generowanie labiryntów o róĝnych elementach. Oto wersja operacji CreateMaze, w której rozwiÈzaliĂmy ten problem przez zastosowanie obiektu MazeFactory jako parametru: Maze* MazeGame::CreateMaze (MazeFactory factory) { Maze* aMaze = factory.MakeMaze(); Room* r1 = factory.MakeRoom(1); Room* r2 = factory.MakeRoom(2); Door* aDoor = factory.MakeDoor(r1, r2); aMaze- AddRoom(r1); aMaze- AddRoom(r2); r1- SetSide(North, factory.MakeWall()); r1- SetSide(East, aDoor); r1- SetSide(South, factory.MakeWall()); r1- SetSide(West, factory.MakeWall()); r2- SetSide(North, factory.MakeWall()); r2- SetSide(East, factory.MakeWall()); r2- SetSide(South, factory.MakeWall()); r2- SetSide(West, aDoor); return aMaze; } Kup książkęPoleć książkę FABRYKA ABSTRAKCYJNA (ABSTRACT FACTORY) 1107 Moĝemy utworzyÊ klasÚ EnchantedMazeFactory (fabrykÚ magicznych labiryntów) jako pod- klasÚ klasy MazeFactory. Klasa EnchantedMazeFactory powinna przesïaniaÊ kilka funkcji skïadowych i zwracaÊ róĝne podklasy klas Room, Wall itd. class EnchantedMazeFactory : public MazeFactory { public: EnchantedMazeFactory(); virtual Room* MakeRoom(int n) const { return new EnchantedRoom(n, CastSpell()); } virtual Door* MakeDoor(Room* r1, Room* r2) const { return new DoorNeedingSpell(r1, r2); } protected: Spell* CastSpell() const; }; Teraz zaïóĝmy, ĝe chcemy utworzyÊ grÚ z labiryntem, w której w pomieszczeniach mogÈ znajdowaÊ siÚ bomby. JeĂli bomba wybuchnie, uszkodzi co najmniej Ăciany. Moĝemy dodaÊ podklasÚ klasy Room sïuĝÈcÈ do rejestrowania, czy w pokoju znajduje siÚ bomba i czy juĝ wy- buchïa. Potrzebna bÚdzie teĝ podklasa klasy Wall do Ăledzenia uszkodzeñ Ăcian. Nazwijmy te klasy RoomWithABomb i BombedWall. Ostatnia klasa, którÈ zdefiniujemy, to BombedMazeFactory. Jest to podklasa klasy MazeFactory gwarantujÈca, ĝe Ăciany to obiekty BombedWall, a pomieszczenia to obiekty RoomWithABomb. W klasie BombedMazeFactory trzeba przesïoniÊ tylko dwie funkcje: Wall* BombedMazeFactory::MakeWall () const { return new BombedWall; } Room* BombedMazeFactory::MakeRoom(int n) const { return new RoomWithABomb(n); } Aby zbudowaÊ prosty labirynt zawierajÈcy bomby, wystarczy wywoïaÊ operacjÚ CreateMaze i przekazaÊ do niej obiekt klasy BombedMazeFactory. MazeGame game; BombedMazeFactory factory; game.CreateMaze(factory); Operacja CreateMaze moĝe przyjmowaÊ takĝe egzemplarz klasy EnchantedMazeFactory, jeĂli ma utworzyÊ magiczny labirynt. Zauwaĝmy, ĝe klasa MazeFactory jest jedynie kolekcjÈ metod wytwórczych. Jest to najczÚĂciej stosowany sposób implementowania wzorca Fabryka abstrakcyjna. Ponadto warto zwróciÊ uwagÚ na to, ĝe klasa MazeFactory nie jest abstrakcyjna. Dlatego peïni jednoczeĂnie funkcje klas AbstractFactory oraz ConcreteFactory. Jest to nastÚpna czÚsto uĝywana implementacja w prostych zastosowaniach wzorca Fabryka abstrakcyjna. Poniewaĝ MazeFactory to klasa konkretna skïadajÈca siÚ w caïoĂci z metod wytwórczych, ïatwo jest utworzyÊ nowÈ klasÚ tego rodzaju przez utworzenie podklasy i przesïoniÚcie operacji, które trzeba zmodyfikowaÊ. Kup książkęPoleć książkę 108 Rozdziaï 3. • WZORCE KONSTRUKCYJNE W operacji CreateMaze wykorzystaliĂmy operacjÚ SetSide obiektów Room do okreĂlenia stron w tych obiektach. JeĂli operacja CreateMaze tworzy pomieszczenia za pomocÈ klasy BombedMazeFactory, labirynt bÚdzie skïadaï siÚ z obiektów RoomWithABomb ze stronami typu BombedWall. JeĂli obiekt RoomWithABomb bÚdzie musiaï uzyskaÊ dostÚp do specyficznej dla podklasy skïadowej obiektu BombedWall, konieczne bÚdzie zrzutowanie referencji do Ăcian z typu Wall* na BombedWall*. To rzutowanie w dóï jest bezpieczne, jeĂli argument rzeczywiĂcie ma typ BombedWall. Jest to pewne, jeĝeli Ăciany sÈ zbudowane wyïÈcznie za pomocÈ klasy BombedMazeFactory. JÚzyki z dynamicznÈ kontrolÈ typu, na przykïad Smalltalk, oczywiĂcie nie wymagajÈ rzutowania w dóï, jednak mogÈ generowaÊ bïÚdy czasu wykonania, jeĂli natrafiÈ na obiekt Wall w miej- scu, gdzie oczekujÈ podklasy klasy Wall. Wykorzystanie przy tworzeniu Ăcian wzorca Fabryka abstrakcyjna pomaga zapobiec podobnym bïÚdom czasu wykonania, poniewaĝ mamy wtedy pewnoĂÊ, ĝe program utworzy Ăciany okreĂlonego typu. Rozwaĝmy wersjÚ klasy MazeFactory w jÚzyku Smalltalk. Obejmuje ona jednÈ operacjÚ make, która przyjmuje jako parametr rodzaj generowanego obiektu. Ponadto fabryka konkretna przechowuje klasy tworzonych produktów. Najpierw naleĝy napisaÊ odpowiednik operacji CreateMaze w jÚzyku Smalltalk: CreateMaze: aFactory | room1 room2 aDoor | room1 := (aFactory make: #room) number: 1. room2 := (aFactory make: #room) number: 2. aDoor := (aFactory make: #door) from: rooml to: room2. room1 atSide: #tnorth put: (aFactory make: #wall). room1 atSide: #east put: aDoor. room1 atSide: #tsouth put: (aFactory make: #wall). room1 atSide: #twest put: (aFactory make: #wall). room2 atSide: #north put: (aFactory make: #wall). room2 atSide: #east put: (aFactory make: #wall). room2 atSide: #tsouth put: (aFactory make: #wall). room2 atSide: #twest put: aDoor. ^ Maze new addRoom: rooml; addRoom: room2; yourself Klasa MazeFactory — jak opisaliĂmy to w punkcie Implementacja — wymaga tylko jednej zmiennej egzemplarza, partCatalog, aby udostÚpniÊ katalog, którego kluczami sÈ klasy kom- ponentów labiryntu. Przypomnijmy teĝ, jak zaimplementowaliĂmy metodÚ make:. make: partName ^ (partCatalog at: partName) new Teraz moĝna utworzyÊ obiekt MazeFactory i wykorzystaÊ go do zaimplementowania operacji createMaze. Do utworzenia fabryki posïuĝy metoda createMazeFactory klasy MazeGame. createMazeFactory ^ (MazeFactory new addPart: Wall named: #wall; addPart: Room named: #room; addPart: Door named: #door; yourself) Kup książkęPoleć książkę FABRYKA ABSTRAKCYJNA (ABSTRACT FACTORY) 1109 Obiekty BombedMazeFactory i EnchantedMazeFactory sÈ tworzone przez powiÈzanie róĝnych klas z odpowiednimi kluczami. Na przykïad obiekt EnchantedMazeFactory moĝna utworzyÊ tak: createMazeFactory ^ (MazeFactory new addPart: Wall named: #wall; addPart: EnchantedRoom named: #room; addPart: DoorNeedingSpell named: #door; yourself) ZNANE ZASTOSOWANIA W pakiecie InterViews do okreĂlania klas AbstractFactory sïuĝy przyrostek Kit [Lin92]. Pakiet ten obejmuje definicje fabryk abstrakcyjnych WidgetKit i DialogKit generujÈcych obiekty interfejsu uĝytkownika specyficzne dla danego standardu wyglÈdu i dziaïania. Pakiet InterViews obejmuje teĝ klasÚ LayoutKit, która w zaleĝnoĂci od wybranego ukïadu tworzy róĝne obiekty zïoĝone. Na przykïad ukïad poziomy moĝe wymagaÊ zastosowania odmien- nych obiektów zïoĝonych w zaleĝnoĂci od orientacji dokumentu (pionowej lub poziomej). W platformie ET++ [WGM88] wzorzec Fabryka abstrakcyjna zastosowano do zapewnienia przenoĂnoĂci rozwiÈzañ miÚdzy róĝnymi systemami okienkowymi (na przykïad X Windows i SunView). Abstrakcyjna klasa bazowa WindowSystem definiuje interfejs do tworzenia obiek- tów reprezentujÈcych zasoby systemów okienkowych (interfejs ten obejmuje na przykïad ope- racje MakeWindow, MakeFont, MakeColor itd.). W podklasach konkretnych interfejs ten jest zaimplementowany dla okreĂlonych systemów okienkowych. W czasie wykonywania pro- gramu platforma ET++ tworzy egzemplarz podklasy konkretnej klasy WindowSystem, a eg- zemplarz ten generuje obiekty konkretne reprezentujÈce zasoby systemowe. POWIkZANE WZORCE Fabryki abstrakcyjne czÚsto sÈ implementowane za pomocÈ metod wytwórczych (Metoda wytwórcza, s. 110), jednak moĝna wykorzystaÊ do tego takĝe wzorzec Prototyp (s. 120). Fabryki konkretne sÈ czÚsto singletonami (Singleton, s. 130). Kup książkęPoleć książkę 110 Rozdziaï 3. • WZORCE KONSTRUKCYJNE METODA WYTWÓRCZA (FACTORY METHOD) klasowy, konstrukcyjny PRZEZNACZENIE OkreĂla interfejs do tworzenia obiektów, przy czym umoĝliwia podklasom wyznaczenie klasy danego obiektu. Metoda wytwórcza umoĝliwia klasom przekazanie procesu tworzenia egzem- plarzy podklasom. INNE NAZWY Konstruktor wirtualny (ang. virtual constructor). UZASADNIENIE W platformach klasy abstrakcyjne sïuĝÈ do definiowania i podtrzymywania relacji miÚdzy obiektami. Platforma czÚsto odpowiada takĝe za tworzenie obiektów. Zastanówmy siÚ nad platformÈ dla aplikacji potrafiÈcych wyĂwietlaÊ wiele dokumentów. Dwie kluczowe abstrakcje w tej platformie to klasy Application i Document. Obie te klasy sÈ abstrakcyjne, a w klientach trzeba utworzyÊ ich podklasy i umieĂciÊ tam implementacje specy- ficzne dla aplikacji. Aby utworzyÊ aplikacjÚ do rysowania, naleĝy zdefiniowaÊ klasy Drawing ´Application i DrawingDocument. Klasa Application odpowiada za zarzÈdzanie obiektami Document i tworzy je na ĝÈdanie (na przykïad kiedy uĝytkownik wybierze z menu opcjÚ Otwórz lub Nowy). Poniewaĝ okreĂlona podklasa klasy Document, której egzemplarz naleĝy utworzyÊ, jest specy- ficzna dla aplikacji, w klasie Application nie moĝna z góry ustaliÊ rodzaju tej podklasy. Klasa Application potrafi jedynie okreĂliÊ, kiedy naleĝy utworzyÊ nowy dokument, a nie jakiego rodzaju powinien on byÊ. Stawia nas to przed dylematem — platforma musi tworzyÊ egzemplarze klas, ale ma informacje tylko o klasach abstrakcyjnych, których egzemplarzy wygenerowaÊ nie moĝe. RozwiÈzaniem jest zastosowanie wzorca Metoda wytwórcza. Pozwala on zakapsuïkowaÊ infor- macje o tym, którÈ podklasÚ klasy Document naleĝy utworzyÊ, i zapisaÊ te dane poza platformÈ. Kup książkęPoleć książkę METODA WYTWÓRCZA (FACTORY METHOD) 1111 W podklasach klasy Application naleĝy przedefiniowaÊ operacjÚ CreateDocument klasy Application, tak aby nowa wersja operacji zwracaïa odpowiedniÈ podklasÚ klasy Document. Egzemplarz podklasy klasy Application moĝe nastÚpnie generowaÊ specyficzne dla aplikacji egze
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

Wzorce projektowe. Elementy oprogramowania obiektowego wielokrotnego użytku
Autor:
, , ,

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: