Narzędzia użytkownika

Narzędzia witryny


fabryka_obiektow

Różnice

Różnice między wybraną wersją a wersją aktualną.

Odnośnik do tego porównania

Both sides previous revision Previous revision
Next revision
Previous revision
fabryka_obiektow [2008/04/16 01:28]
sdybiec usunięto
fabryka_obiektow [2008/04/16 21:42]
kzbikows
Linia 1: Linia 1:
 <code cpp> <code cpp>
-/*********************************************************************************** +/*Kamil Żbikowski - gr H5ISI - PRACA DOMOWA Z PRZEDMIOTU ZPR | 
- *                                                            Autor: Sławomir Dybiec +|_______________________________________________________________________________| 
- * Wzorzec: ​        ​fabryka prototypów (rejestrowanie obiektów) +| Fabryka Obiektów - rejestracja metod fabrycznych | 
-  +| | 
- * ZastosowanieWzorca tego używamy, kiedy mamy informacje o typie obiektu ​który  +|Problem Tworzenie ​obiektu ​jest jezyku c++ statytyczne. Nie zawiera on | 
- * chcemy utworzyć. Programista ​momencie pisania kodu nie może przewidzieć,​ jakiej +| mechanizmu tworzenia obiektów w sposób dynamiczny. Pojęcia ​klasy
- klasy obiekt należy utworzyćInformacja ta znana będzie dopiero podczas wykonywania ​ +| i obiektu są różneKlasa jest tworzona przez programistę - jest| 
- programu(np. na podstawie informacji zawartych w pliku).  +| definicją obiektu, który to z kolei jest tworzony w trakcie | 
- *  ​ +| działania ​programu. Często jednak pojawia się potrzeba aby | 
- * OpisWzorzec ten jest właściwie połączeniem wzorca ​fabryki obiektów ​oraz wzorca +| w sposób dynamiczny generować obiekty ​(np. odczyt/​zapis obiektów| 
- * prototypu. Uwaga fabryka prototypów często jest również singletonemZachęcam ​do +| na dyskalbo nppozostawić klientom możliwość modyfikacji | 
- * zapoznania się z wymienionymi przeze mnie wzorcami+| zachowania obiektów za pomocą polimorfizmu poprzez wywołanie ​ | 
-  +| funkcji wirtualnej Create zdefiniowanej przez klasę wysokiego | 
- * PrzykładZałóżmyże posiadamy klasy słące do przetwarzania plików graficznych,​ +| poziomu. Rozwiązanie "​siłowe"​ poprzez zastosowanie instrukcji | 
- * jednak inna klasa odpowiada za obsługę obrazów zapisanych w formacie GIFa innej +| switch nie jest najlepszym rozwiązaniem:
- ​* ​klasy używamy do obrazów zapisanych jako JPGW momencie tworzenia kodu nie wiemy, +| - mija sie z ideą obiektowości | 
- * jakiego typu będzie ​plik który zechce przetworzyć użytkownik naszej biblioteki. +| - w jednym pliku źródłowym ​jest zawarta informacja | 
- * W tym momencie ​pomocą przychodzi nam wzorzec fabryki prototypów+|   o wszystkich podtypach co czyni go wąskim gardłem, | 
-*/ +|   jeśli chodzi o zależności kompilacji i utrzmanie kodu.| 
 +| - modyfiukując kod musimy go zmieniać w wielu miejscach | 
 +|Rozwiąznie:​ Naprzeciw powyższemu problemowi wychodzi wzorzec ​fabryki ​
 +| obiektów ​z rejestracją metod fabrycznych,​ który w przybliżeniu | 
 +| wygląda obrazuje poniższy schemat:​ | 
 +| _______ _______________________ |  
 +| |Produkt|<​--------------| ​    ​FabrykaObiektow | | 
 +| |_______| |-----------------------| | 
 +|     ^ |+ StworzProdukt() | | 
 +|     | |+ RejestrujProduct() | | 
 +|     | |_______________________| | 
 +| ___|_____ | 
 +| |Produkt ​ | |  
 +| |Konkretny| | 
 +| |_________| | 
 +| | 
 +| Produkt konkretny - fabryka dostarcza produkt w postaci obiektu | 
 +| Produkt - abstrakcyjny typ podstawowyFabryka zwraca wskaźnik | 
 +|   ​do tego obiektu nie przekazując informacji o produkcie| 
 +|   konkretnym. | 
 +| Informacje o konkretnym produkcie przechowywane są przez fabrykę| 
 +| w mapie (np. std::map). Każdy konkretny produkt ma przydzielony | 
 +| unikalny identyfiaktor na podstawiektórego jest identyfikowany| 
 +| Własciwie jest przechowywany wskaźnik do funkcji zwracającej | 
 +| wskaźnik ​do Produkt. | 
 +| Rejestracja kolejnego produktu konkretnego odbywa się poprzez | 
 +| podanie unikalnego identyfikatora danego Produktu Konkretnego | 
 +| oraz wskaźnika na funkcję zwracającą wskaźnik na Produkt. | 
 +| Dzięki zastosowaniu polimorfizmu wyeliminowaliśmy wady | 
 +| rozwiązania siłowego. Teraz dodanie kolejnego Produktu | 
 +| Konkretnego sprowadza się do stworzenia nowej klasy definiującej| 
 +| gozawierającej metodę postaci:​ | 
 +| ProduktStworzKonkretnyProdukt(){ | 
 +| return new ProduktKonkretny;​ | 
 +| } | 
 +| Rejestracja natomiast polega na wywolaniu RegisterProduct na | 
 +| rzecz wyżej wymienionej metodyOd tej chwili stworzenie | 
 +| konkretnego produktu ​będzie ​polegało na wywołaniu metody | 
 +| StworzProdukt ​okrślonym id podanym w porcesie rejstracji | 
 +| Czesto fabryka obiektów powinna być singletonem,​ ale to juz jest| 
 +| temat na oddzielną pracę
 +|______________________________________________________________________________*/
 #include <​iostream>​ #include <​iostream>​
-#include <vector>​ +#include <map>
-#include <​assert.h>+
  
-using namespace std;// dla wygodyżeby nie pisać wszędzie std +/** 
- +  * Szablon fabryki. Product - klasa abstrakcyjanId - identyfiaktor klasy, ProductCreator -  
-//Typ pliku będzie reprezentowany poprzez ​obiekt ​string zawierający jego rozszerzenie ​ +  * wskaźnik na funkcję zwracającą wskaźnik na obiekt ​konkretny dziedziczący po Product 
-typedef std::string IMG_TYPE; +  */ 
- +template <class Product, class Id, class ProductCreator = Product* (*)()> 
-//Funkcja pomocnicza zwracająca nam rozszerzenie pliku.  +class Factory{ 
-string getFileExtension(const ​string ​filename) + public: 
-+ //Metoda produkująca konkretne obiekty na podstawie ich identyfiaktorów
- string w; + Product* CreateProduct(const ​Idid){ 
- int a; + typename Map::​const_iterator i associations.find(id); 
- a=filename.find_last_of("​."​); + if(i !associations.end())
- if (a==-1 || a==static_cast<​int>​(filename.size())-1+ return (i->second());  //jeśli w mapie jest podany w parametrze klucz 
- +        //wywołaj powiązaną z nim funkcję 
- //Błąd plik nie posiada rozszerzenia +
- assert(0);​  + else 
-+ return NULL//obsługa błędów - przykładowe rozwiązanie 
- w.assign(filename,​ a+1, filename.size()- a-1)+      //można zaimplementować rozwiązanie z użyciem wyjątków 
- //Zwracamy rozszerzenie zapisane tylko dużymi literami +
- std::​transform(w.begin()w.end(), w.begin(), ::toupper); + //Metoda rejestrująca obiekty ​fabryce. 
- return w; + bool Register(const Id& idProductCreator creator)
-+ return associations.insert( 
- + typename Map::value_type(id,​ creator)).second
-//Klasa reprezentująca dowolny obrazek+
-class Image  + //Metoda wyrejestrowująca obiekty
-+ bool UnRegister(const Id& id)
-public: + return associations.erase(id) == 1
-   //​Metoda zwracająca nam wskaźnik na konkretną instancje obiektu Image. +
-   ​virtual Image* Clone()=0+ private: 
-   virtual string getName()=0+ typedef std::​map<​Id,​ ProductCreator>​ Map;  
-   ​/+ Map associations; ​//mapa służy do połączenia identyfiatora z danym wskźnikiem na funkcję 
-    * W tym miejscu definiujemy metody obsługujące obrazek. Oczywiście ​są to także +   //wymagane jest aby Id miało zaimplementowane operatory '​==',​ '<'​ 
-    * metody wirtualne. +   //
-    *+
 }; };
-/+//Klasa abstrakcyjna Ford. Diedziczą po niej konkretne modele samochodów,​ które rejestrują się fabryce 
-Klasa reprezentująca obrazek zapisany ​formacie JPG. Jest to jednocześnie +//a następnie są przez nią produkowane
-* implementacja wzorca prototyp. +class Ford
-*/ + public: 
-class ImageJPG : public Image { + //funkcja, która w każdej z klas dziedziczących po Ford  
-   //Funkcja zwracająca nam wskaźnik na nowy obiekt klasy ImageJPG+ //​wyświetla rodzaj danego obiektu 
-   Image* Clone()  + virtual void Info() = 0;
-   +
-      ​return new ImageJPG(*this);​  +
-   } +
-   ​//Funkcja zwracająca srting z opisem klasy +
-   string getName() +
-   { +
-      return string("Klasa przetwarzająca pliki z rozszerzeniem JPG"); +
-   }+
 }; };
- +Factory<​Ford,​ int> FordFactory; ​//obiekt relizujący fabrykę samochodów marki Ford 
-//Podobnie jak klasa ImageJPG. +//Każda z poniższych przestrzeni nazw zawierająca konkretne implementacje  
-class ImageGIF : public Image { +//klasy Ford może znajdować się w osobnym pliku. Chcąc dodać kolejną markę samochodu 
-   ​Image* Clone()  +//dodajemy kolejny plik zawartością ​podobną jak każdy z namespace'​ow.  
-   { +namespace Focus{ 
-      return new ImageGIF(*this);​  + static const int ID = 1; 
-   } + class ​FocusFord : public Ford
-   ​string getName() + public: 
-   { + virtual void Info(){ 
-      return string("​Klasa przetwarzająca pliki z rozszerzeniem GIF"​);​ + std::cout << "​Jestem Fordem Focus"<<​std::​endl
-   } +
-}; + private
-/+ //konkretne właściwości danego modelu 
-* Klasa pomocnicza wiążąca ​prototyp ​klasy przetwarzającej obraz odpowiednią wartością + }
-* pola IMG_TYPE+ FordCreateFocusFord(){ 
-*/ + return ​new FocusFord
-class ImagePrototype +
-+ void StartProduce(){ 
-public: + FordFactory.Register(Focus::ID&​Focus::​CreateFocusFord); 
-   ImagePrototype(IMG_TYPE t, Image *p):​type(t),​prototype(p){} +
-   ​IMG_TYPE type; + void StopProduce(){ 
-   Image *prototype;​ + FordFactory.UnRegister(Focus::ID); 
-}; + }
-//Klasa, której używamy do tworzenia obiektów. +
-class ImagePrototypeFactory +
-{  +
-   ​std::vector<ImagePrototype>​ prototypes+
-public+
-   ​/+
-   * Funkcja służąca do dodawania nowych prototypów klas do przetwarzania obrazów. +
-   * Prototypy są powiązane z odpowiednią wartością pola IMG_TYPE. +
-   */ +
-   void addPrototype( IMG_TYPE img_type, Image * prototype ) +
-   { +
-      ImagePrototype ip = ImagePrototype(img_type,​ prototype)+
-      ​prototypes.push_back( ip ); +
-   } +
-   //​Funkcja zwracająca wskaźnik ​ na obiekt, który potrafi obsługiwać zadany typ obrazka. +
-   ​Image ​*getImageIMG_TYPE img_type ​) +
-   +
-      int a = static_cast<​int>​(prototypes.size());​ +
-      for(int i=0; i < a; ++i) +
-         ​if(prototypes[i].type == img_type) +
-            ​return ​prototypes[i].prototype->​Clone()+
- +
-      //Błąd nieznany typ obrazka +
-      return NULL; +
-   +
-}; +
- +
-int main () +
-{  +
-   //Tworzymy fabrykę, która będzie konstruować odpowiedni obiekt w zależności od typu pliku. +
-   ​ImagePrototypeFactory ipf; +
-   Image * a; +
-   //​Dodajemy prototypy klas, potrafiących przetwarzać odpowiednie pliki. +
-   ​ipf.addPrototype("​GIF"​new ImageGIF()); +
-   ipf.addPrototype("​JPG",​ new ImageJPG()); +
-  +
-   //​Prosimy Fabrykę prototypów o wskaźnik na obiekt klasy potrafiący przetwarzać plik xxx.gif +
-   a = ipf.getImage(getFileExtension("​xxx.gif"​));​ +
-   //​Prosimy klasę o przedstawienie się :) +
-   cout << a->​getName() << endl+
- +
-   ​return 0; +
-   +
-  /* +
-  * Wyjście: +
-  * Klasa przetwarzająca pliki z rozszerzeniem GIF +
-  */+
 } }
- +namespace Galaxy{ 
- /* + static const int ID = 2; 
- ​* ​Przy asnej implementacji tego wzorca, należy pamiętać o mechanizmie obsługi wyjątków, + class GalaxyFord : public Ford{ 
- ​* ​który tutaj został pominięty+ public: 
- * Innym częstym przykładem ​użycia ​tego wzorca jest zapis stanu naszego programu do pliku, + virtual void Info(){ 
- * wtedy podobnie jak wyżej wymienionym przykładzie dla każdej ​klasy definiujemy + std::​cout << "​Jestem Fordem Galaxy"<<​std::​endl;​ 
- ​* ​odpowiedni identyfikator. Zapis każdego obiektu do pliku poprzedzony jest zapisaniem +
- ​* ​identyfikatora klasy do której należy obiektNastępnie przy odczycie przekazujemy ten + private:​ 
- * identyfikator do odpowiedniej funkcji fabryki prototypów(w pokazanym przeze mnie + //konkretne właściwości danego modelu 
- * przykładzie była to funkcja getImage()), która ​to funkcja zwraca nam wskazanie na nowo + }; 
- * utworzony obiekt klasy powiązanej z tym identyfikatorem+ FordCreateGalaxyFord(){ 
- * Ważną cechą zaprezentowanego przeze mnie wzorca jest łatwość rozbudowy programu ​dalsze klasy+ return new GalaxyFord;​ 
- * Nowo napisaną klasę(musi ona implementować wzorzec prototypurejestrujemy w naszej fabryce poprzez +
- * wywołanie funkcji addPrototype. + void StartProduce(){ 
- *+ FordFactory.Register(Galaxy::​ID,​ &​Galaxy::​CreateGalaxyFord);​ 
- </​code>​+
 + void StopProduce(){ 
 + FordFactory.UnRegister(Galaxy::​ID);​ 
 +
 +
 +namespace Mondeo{ 
 + static const int ID = 3; 
 + class MondeoFord : public Ford{ 
 + public: 
 + virtual void Info(){ 
 + std::​cout << "​Jestem Fordem Mondeo"<<​std::​endl;​ 
 +
 + private:​ 
 + //​konkretne ​aściwości danego modelu 
 + }; 
 + FordCreateMondeoFord(){ 
 + return new MondeoFord;​ 
 +
 + void StartProduce(){ 
 + FordFactory.Register(Mondeo::​ID,​ &​Mondeo::​CreateMondeoFord);​ 
 +
 + void StopProduce(){ 
 + FordFactory.UnRegister(Mondeo::​ID);​ 
 +
 +
 +//​Przyklady ​użycia ​fabryki 
 +int main(){ 
 + //Trzy wskaźniki na obiekty typu Ford. Bedą się nich znajdowały wskazania na konkretne ​klasy pochodne klasy Ford. 
 + Fordany1;  
 + Fordany2;  
 + any1 = FordFactory.CreateProduct(Focus::​ID);​ //​Próbujemy wyprodukować Forda Focus'​a 
 + if(any1) 
 + any1->​Info();​ //​jeśli się udałto wyświetl informacje o samochodzie 
 + //​oczywiście w tym wypadku operacja się nie uda, ponieważ nie  
 + //​zarejestrowaliśmy tego podtypu w fabryce 
 + else 
 + //jeśli nie to wypisz stosowny komunikat 
 + std::​cout<<"​Fabryka nie produkuje tego typu samochodu"<<​std::​endl;​ 
 + //​rejestrujemy podtyp w fabryce 
 + Focus::​StartProduce()
 + any1 = FordFactory.CreateProduct(Focus::​ID)
 + if(any1) 
 + any1->​Info();​ //​jeśli się udało to wyświetl informacje o samochodzieteraz już dokonaliśmy rejestracji 
 + else 
 + //jeśli nie to wypisz stosowny komunikat 
 + std::​cout<<"​Fabryka nie produkuje tego typu samochodu"<<​std::​endl;​ 
 + //​Zaczynamy produkować modele Galaxy 
 + Galaxy::​StartProduce();​ 
 + any2 = FordFactory.CreateProduct(Galaxy::​ID);​ //​produkujemy 1 egzemplarz Galaxy 
 + if(any2) 
 + any2->​Info();​ //​jeśli się udało to wyświetl informacje ​samochodzie,​ teraz już dokonaliśmy rejestracji 
 + else 
 + //jeśli nie to wypisz stosowny komunikat 
 + std::​cout<<"​Fabryka nie produkuje tego typu samochodu"<<​std::​endl;​ 
 + Galaxy::​StopProduce();​ 
 + Focus::​StopProduce();​ 
 + //staramy sie wyprodukować Galaxy i Focus po wyrejestrowaniu ich z fabryki 
 + any1 = FordFactory.CreateProduct(Focus::​ID);​ 
 + any2 = FordFactory.CreateProduct(Galaxy::​ID);​ 
 + if(any2) 
 + any2->​Info();​ //​jeśli się udało to wyświetl informacje o samochodzie,​ teraz już dokonaliśmy rejestracji 
 + else 
 + //jeśli nie to wypisz stosowny komunikat 
 + std::​cout<<"​Fabryka nie produkuje tego typu samochodu"<<​std::​endl;​ 
 + if(any1
 + any2->​Info();​ //​jeśli się udało to wyświetl informacje o samochodzie,​ teraz już dokonaliśmy rejestracji 
 + else 
 + //jeśli nie to wypisz stosowny komunikat 
 + std::​cout<<"​Fabryka nie produkuje tego typu samochodu"<<​std::​endl;​ 
 + return 0; 
 +} 
 +</​code>​
fabryka_obiektow.txt · ostatnio zmienione: 2008/04/16 21:42 przez kzbikows