To jest stara wersja strony!
/*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 | |||
#include <iostream> #include <map>
/**
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;
}