Narzędzia użytkownika

Narzędzia witryny


fabryka_prototypow
/***********************************************************************************
 *                                                            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.
 */
 
fabryka_prototypow.txt · ostatnio zmienione: 2008/04/16 01:28 przez sdybiec