To jest stara wersja strony!
/*** * Autor: Sławomir Dybiec * Wzorzec: fabryka prototypów (rejestrowanie obiektów) * * Zastosowanie: Wzorca tego używamy, kiedy mamy informacje o typie obiektu który * chcemy utworzyć. Programista w momencie pisania kodu nie może przewidzieć, jakiej * klasy obiekt należy utworzyć. Informacja ta znana będzie dopiero podczas wykonywania * programu(np. na podstawie informacji zawartych w pliku). * * Opis: Wzorzec ten jest właściwie połączeniem wzorca fabryki obiektów oraz wzorca * prototypu. Uwaga fabryka prototypów często jest również singletonem. Zachęcam do * zapoznania się z wymienionymi przeze mnie wzorcami. * * Przykład: Załóżmy, że posiadamy klasy służące do przetwarzania plików graficznych, * jednak inna klasa odpowiada za obsługę obrazów zapisanych w formacie GIF, a innej * klasy używamy do obrazów zapisanych jako JPG. W momencie tworzenia kodu nie wiemy, * jakiego typu będzie plik który zechce przetworzyć użytkownik naszej biblioteki. * W tym momencie z pomocą przychodzi nam wzorzec fabryki prototypów. */
#include <iostream> #include <vector> #include <assert.h>
using namespace std; dla wygody, żeby nie pisać wszędzie std Typ pliku będzie reprezentowany poprzez obiekt string zawierający jego rozszerzenie typedef std::string IMG_TYPE;
Funkcja pomocnicza zwracająca nam rozszerzenie pliku. string getFileExtension(const string & filename) { string w; int a; a=filename.find_last_of(„.”); if (a==-1 || a==static_cast<int>(filename.size())-1) { Błąd plik nie posiada rozszerzenia
assert(0); } w.assign(filename, a+1, filename.size()- a-1); //Zwracamy rozszerzenie zapisane tylko dużymi literami std::transform(w.begin(), w.end(), w.begin(), ::toupper); return w;
}
Klasa reprezentująca dowolny obrazek. class Image { public: Metoda zwracająca nam wskaźnik na konkretną instancje obiektu Image.
virtual Image* Clone()=0; virtual string getName()=0; /* * W tym miejscu definiujemy metody obsługujące obrazek. Oczywiście są to także * metody wirtualne. */
}; /* * Klasa reprezentująca obrazek zapisany w formacie JPG. Jest to jednocześnie * implementacja wzorca prototyp. */ class ImageJPG : public Image {
//Funkcja zwracająca nam wskaźnik na nowy obiekt klasy ImageJPG.
Image* Clone()
{
return new ImageJPG(*this);
}
//Funkcja zwracająca srting z opisem klasy
string getName()
{
return string("Klasa przetwarzająca pliki z rozszerzeniem JPG");
}
};
Podobnie jak klasa ImageJPG. class ImageGIF : public Image { Image* Clone() { return new ImageGIF(*this); } string getName() { return string(„Klasa przetwarzająca pliki z rozszerzeniem GIF”); } }; /* * Klasa pomocnicza wiążąca prototyp klasy przetwarzającej obraz z odpowiednią wartością * pola IMG_TYPE. */ class ImagePrototype { public: ImagePrototype(IMG_TYPE t, Image *p):type(t),prototype(p){} IMG_TYPE type; Image *prototype; }; 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 *getImage( IMG_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 */
}
/* * Przy własnej implementacji tego wzorca, należy pamiętać o mechanizmie obsługi wyjątków, * który tutaj został pominięty. * Innym częstym przykładem użycia tego wzorca jest zapis stanu naszego programu do pliku, * wtedy podobnie jak w wyżej wymienionym przykładzie dla każdej klasy definiujemy * odpowiedni identyfikator. Zapis każdego obiektu do pliku poprzedzony jest zapisaniem * identyfikatora klasy do której należy obiekt. Następnie przy odczycie przekazujemy ten * identyfikator do odpowiedniej funkcji fabryki prototypów(w pokazanym przeze mnie * przykładzie była to funkcja getImage()), która to funkcja zwraca nam wskazanie na nowo * utworzony obiekt klasy powiązanej z tym identyfikatorem. * Ważną cechą zaprezentowanego przeze mnie wzorca jest łatwość rozbudowy programu o dalsze klasy. * Nowo napisaną klasę(musi ona implementować wzorzec prototypu) rejestrujemy w naszej fabryce poprzez * wywołanie funkcji addPrototype. */