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;
}