/*Kamil Żbikowski - gr H5ISI - PRACA DOMOWA Z PRZEDMIOTU ZPR | |_______________________________________________________________________________| | Fabryka Obiektów - rejestracja metod fabrycznych | | | |Problem: Tworzenie obiektu jest w jezyku c++ statytyczne. Nie zawiera on | | mechanizmu tworzenia obiektów w sposób dynamiczny. Pojęcia klasy| | i obiektu są różne. Klasa jest tworzona przez programistę - jest| | definicją obiektu, który to z kolei jest tworzony w trakcie | | działania programu. Często jednak pojawia się potrzeba aby | | w sposób dynamiczny generować obiekty (np. odczyt/zapis obiektów| | na dysk) albo np. pozostawić klientom możliwość modyfikacji | | zachowania obiektów za pomocą polimorfizmu poprzez wywołanie | | funkcji wirtualnej Create zdefiniowanej przez klasę wysokiego | | poziomu. Rozwiązanie "siłowe" poprzez zastosowanie instrukcji | | switch nie jest najlepszym rozwiązaniem: | | - mija sie z ideą obiektowości | | - w jednym pliku źródłowym jest zawarta informacja | | 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 podstawowy. Fabryka 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 podstawie, któ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| | go, zawierającej metodę postaci: | | Produkt* StworzKonkretnyProdukt(){ | | return new ProduktKonkretny; | | } | | Rejestracja natomiast polega na wywolaniu RegisterProduct na | | rzecz wyżej wymienionej metody. Od tej chwili stworzenie | | konkretnego produktu będzie polegało na wywołaniu metody | | StworzProdukt z 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 <map> /** * Szablon fabryki. Product - klasa abstrakcyjan, Id - identyfiaktor klasy, ProductCreator - * wskaźnik na funkcję zwracającą wskaźnik na obiekt konkretny dziedziczący po Product */ template <class Product, class Id, class ProductCreator = Product* (*)()> class Factory{ public: //Metoda produkująca konkretne obiekty na podstawie ich identyfiaktorów. Product* CreateProduct(const Id& id){ typename Map::const_iterator i = associations.find(id); if(i != associations.end()){ return (i->second()); //jeśli w mapie jest podany w parametrze klucz //wywołaj powiązaną z nim funkcję } else return NULL; //obsługa błędów - przykładowe rozwiązanie //można zaimplementować rozwiązanie z użyciem wyjątków } //Metoda rejestrująca obiekty w fabryce. bool Register(const Id& id, ProductCreator creator){ return associations.insert( typename Map::value_type(id, creator)).second; } //Metoda wyrejestrowująca obiekty. bool UnRegister(const Id& id){ return associations.erase(id) == 1; } private: typedef std::map<Id, ProductCreator> Map; Map associations; //mapa służy do połączenia identyfiatora z danym wskźnikiem na funkcję //wymagane jest aby Id miało zaimplementowane operatory '==', '<' // }; //Klasa abstrakcyjna Ford. Diedziczą po niej konkretne modele samochodów, które rejestrują się w fabryce //a następnie są przez nią produkowane. class Ford{ public: //funkcja, która w każdej z klas dziedziczących po Ford //wyświetla rodzaj danego obiektu virtual void Info() = 0; }; Factory<Ford, int> FordFactory; //obiekt relizujący fabrykę samochodów marki Ford //Każda z poniższych przestrzeni nazw zawierająca konkretne implementacje //klasy Ford może znajdować się w osobnym pliku. Chcąc dodać kolejną markę samochodu //dodajemy kolejny plik z zawartością podobną jak każdy z namespace'ow. namespace Focus{ static const int ID = 1; class FocusFord : public Ford{ public: virtual void Info(){ std::cout << "Jestem Fordem Focus"<<std::endl; } private: //konkretne właściwości danego modelu }; Ford* CreateFocusFord(){ return new FocusFord; } void StartProduce(){ FordFactory.Register(Focus::ID, &Focus::CreateFocusFord); } void StopProduce(){ FordFactory.UnRegister(Focus::ID); } } namespace Galaxy{ static const int ID = 2; class GalaxyFord : public Ford{ public: virtual void Info(){ std::cout << "Jestem Fordem Galaxy"<<std::endl; } private: //konkretne właściwości danego modelu }; Ford* CreateGalaxyFord(){ return new GalaxyFord; } void StartProduce(){ FordFactory.Register(Galaxy::ID, &Galaxy::CreateGalaxyFord); } 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 właściwości danego modelu }; Ford* CreateMondeoFord(){ 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ę w nich znajdowały wskazania na konkretne klasy pochodne klasy Ford. Ford* any1; Ford* any2; any1 = FordFactory.CreateProduct(Focus::ID); //Próbujemy wyprodukować Forda Focus'a if(any1) any1->Info(); //jeśli się udało 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 samochodzie, teraz 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 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; 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; }