Abstract factory pattern (Fabryka abstrakcyjna)
/*************************************************************************************
* Michał Chodkiewicz *
* Wzorzec: fabryka abstrakcyjna (abstract factory) *
* *
* Zastosowanie: Wzorzec ten umożliwia łatwe wprowadzanie zmian w zakresie klas *
* używanych w kodzie, co umożliwia pisanie kodu, np: na kilka *
* platform jednocześnie, czy łatwiejsze zmienianie kodu wewnątrz *
* klasy bibliotecznej. *
* *
* Opis: Fabryka abstrakcyjna, zwykle tworzona jako interfejs, w języku *
* c++ jest implementowana za pomocą klasy abstrakcyjnej. Klasa ta *
* ma wygląd typowej fabryki, jednak jako typy zwracane przez *
* wirtualne metody fabrykujące ma zadeklarowane klasy abstrakcyjne *
* reprezentujące interfejsy obiektów zwracanych przez klasy *
* pochodne. W kodzie programu implementuje się dla konkretnych *
* zastosowań klasy pochodne od fabryki abstrakcyjnej wytwarzające *
* obiekty odpowiednie dla danego systemu czy pliku *
* konfiguracyjnego. Dzięki temu, że najczęściej fabryka jest *
* tworzona w programie tylko raz, przy zmianie zachowania obiektów *
* wystarczy zmodyfikować tą linijkę w kodzie, w której następuje *
* przypisanie konkretnej fabryki. *
* *
* Przykładowy schemat (schemat opisuje inny przypadek niż kod, dla urozmaicenia): *
* Prezentowany jest przypadek programowania dla kilku systemów operacyjnych *
* *
* Odczyt schematu: *
* [Element na początku strzałki] *
* (d) dziedziczy po *
* (w) zawiera wskaźnik/wskaźniki na *
* (t) tworzy obiekty klasy *
* [Element na który wskazuje strzałka] *
* _______________ ______________________ *
* | KlasaUsera |-------------->|FabrykaAbstrakcyjna | *
* |_____________| (w)|____________________| *
* |_____________| |____________________| *
* |_____________| | A stworzObiekt()=0 | *
* | |____________________| *
* | /|\(d) /|\(d) /|\(d) *
* | ______________| | |________ *
* | _______|__________ __________|_______ ________|_________*
* | |FabrykaUnixowa | |FabrykaDosowa | |FabrykaWindows |*
* -----------------/\----|________________| |________________| |________________|*
* | | |________________| |________________| |________________|*
* | | |A stworzObiekt()| |A stworzObiekt()| |A stworzObiekt()|*
* | |(w) |________________| |________________| |________________|*
* | ___________\|/_____ | | *
* | | A (klasa abstr) | | | *
* | |_________________| | | *
* | |_________________| | | *
* | | zareaguj() | | | *
* | |_________________| | | *
* | /|\(d) /|\(d) /|\(d) | | *
* |(t)___| | |_______ | | *
* \|/__|______ ___|________ _____|______ | | *
* | AUnix | | ADos | | AWindows | | | *
* |__________| |__________| |__________| (t) | | *
* |__________| |__________| |__________|<-------------/\-----------------| *
* |zareaguj()| |zareaguj()| |zareaguj()| | *
* |__________| |__________| |__________| | *
* /|\(t) | *
* |________________________________| *
* *
*************************************************************************************/
/* Przykładowy kod. Przedstawia on rysowanie obiektów w zależności od
szybkości procesora */
#include <iostream>//Biblioteka potrzebna w tym przykładzie
using namespace std;// dla wygody, żeby nie pisać wszędzie std
// Abstrakcyjny model obiektu graficznego
class ObiektGraficznyAbstrakcyjny {
protected:
float x_;// położenie na ekranie w osi x
float y_;// położenie na ekranie w osi y
public:
virtual void rysujSie()=0;// metoda do rysowania obiekt
// poniższe metody są wirtualne, gdyż klasy pochodne nie muszą koniecznie w ten sposób przechowywać
// położenia obiektu, jednak mają już ciała, gdyż to będzie najczęstszy przypadek
// metoda do ustalania położenia obiektu
virtual void ustawPozycje(float x, float y){
x_=x;
y_=y;
}
// metody do pobierania położenia obiektu
virtual float podajPozX() {return x_;}
virtual float podajPozY() {return y_;}
ObiektGraficznyAbstrakcyjny (float x, float y) : x_(x), y_(y){}
virtual ~ObiektGraficznyAbstrakcyjny() {}//wirtualny destruktor, bo są wirtualne funkcje
};
// Abstrakcyjna fabryka obiektów, służąca za interfejs
class FabrykaObiektowGraficznychAbstrakcyjna {
public:
// metoda do tworzenia instancji obiektów
virtual ObiektGraficznyAbstrakcyjny * utworzObiekt(float x, float y)=0;
virtual ~FabrykaObiektowGraficznychAbstrakcyjna(){}//wirtualny destruktor, bo są wirtualne funkcje
};
// Konkretny model obiektu graficznego, dla wolniejszych komputerów
class ObiektGraficznyProsty : public ObiektGraficznyAbstrakcyjny {
public:
// funkcja rysujSie jest mocno okrojona, gdyż to tylko przykład zastosowania
void rysujSie(){
cout<<"malo wymagajacy model jest rysowany na pozycji: "<<x_<<", "<<y_<<endl;
}
ObiektGraficznyProsty (float x, float y):ObiektGraficznyAbstrakcyjny(x,y){}
};
// Konkretny model obiektu graficznego, dla szybszych komputerów
class ObiektGraficznyTrudny : public ObiektGraficznyAbstrakcyjny {
public:
// funkcja rysujSie jest mocno okrojona, gdyż to tylko przykład zastosowania
void rysujSie(){
cout<<"BARDZO WYMAGAJACY MODEL JEST RYSOWANY NA POZYCJI: "<<x_<<", "<<y_<<endl;
}
ObiektGraficznyTrudny (float x, float y):ObiektGraficznyAbstrakcyjny(x,y){}
};
//Konkretna fabryka obiektów graficznych, dla wolnych komputerów
class FabrykaObiektowGraficznychProstych : public FabrykaObiektowGraficznychAbstrakcyjna {
public:
// skonkretyzowanie metody
ObiektGraficznyAbstrakcyjny * utworzObiekt(float x, float y) {
return new ObiektGraficznyProsty(x,y);
}
};
//Konkretna fabryka obiektów graficznych, dla szybkich komputerów
class FabrykaObiektowGraficznychTrudnych : public FabrykaObiektowGraficznychAbstrakcyjna {
public:
// skonkretyzowanie metody
ObiektGraficznyAbstrakcyjny * utworzObiekt(float x, float y) {
return new ObiektGraficznyTrudny(x,y);
}
};
// Klasa korzystająca z fabryk
class KlasaUzytkowa {
private:
static const int ILE_OBIEKTOW=5;//ile obiektow graficznych naraz na ekranie maksymalnie
// Wskaznik na fabryke obiektow, tu bedzie podany wskaznik do konkretnej fabryki
FabrykaObiektowGraficznychAbstrakcyjna * fabryka_;
ObiektGraficznyAbstrakcyjny * pulaObiektow_[ILE_OBIEKTOW]; // Tablica obiektow do rysowania
public:
// dla zapełniania tablicy obiektów, zmienna nowa partia mówi czy zmieniać obiekty,
// czy też tworzyć nowe od poczatku
void pobierzNowe(bool nowaPartia){
for (int licznik=0; licznik<5; ++licznik){
ObiektGraficznyAbstrakcyjny * temp=pulaObiektow_[licznik];
if (temp!=NULL){// odtworzenie obiektow w nowych warunkach
pulaObiektow_[licznik]=fabryka_->utworzObiekt(temp->podajPozX(), temp->podajPozY());
delete temp;
}
else if (nowaPartia){
pulaObiektow_[licznik]=fabryka_->utworzObiekt(licznik, ILE_OBIEKTOW - licznik);
}
}
}
// dla ustawienia źródła obiektów
void ustawFabryke(FabrykaObiektowGraficznychAbstrakcyjna * nowa_fabryka) {
delete fabryka_;// usunięcie starej fabryki
fabryka_=nowa_fabryka; // ustawienie nowej fabryki
pobierzNowe(false);//żeby od razu zmienić typ przechowywanych obiektów
}
// dla rysowania obiektów
void rysujObiekty(){
for (int licznik=0; licznik<ILE_OBIEKTOW; ++licznik){
if (pulaObiektow_[licznik]!=NULL){
pulaObiektow_[licznik]->rysujSie();
}
}
}
// konstruktor
KlasaUzytkowa (FabrykaObiektowGraficznychAbstrakcyjna *nowa_fabryka){
fabryka_=nowa_fabryka;
}
// destruktor
~KlasaUzytkowa(){
for (int licznik=0; licznik<5; ++licznik){
delete pulaObiektow_[licznik];// zwolnienie puli obiektów
}
delete fabryka_;// zwolnienie fabryki
}
};
//Program testowy
int main(){
cout<<"..:: Uruchamianie programu w trybie wysokiej jakosci grafiki ::.."<<endl;
// Na początek ustawiany bardziej wymagający typ obiektu graficznego
KlasaUzytkowa * testowa=new KlasaUzytkowa(new FabrykaObiektowGraficznychTrudnych);
testowa->pobierzNowe(true);// Ustawianie przykładowych obiektów
cout<<"..:: Rysowanie przykladowych obiektow ::.."<<endl;
testowa->rysujObiekty();
//{...} Tutaj okazało się, że niestety maszyna jest za wolna
cout<<"..:: Niestety maszyna jest za wolna, przelaczanie w tryb niskiej jakosci::.."<<endl;
testowa->ustawFabryke(new FabrykaObiektowGraficznychProstych());
cout<<"..:: Rysowanie przykladowych obiektow ::.."<<endl;
testowa->rysujObiekty();
cout<<"..:: Koniec programu ::.."<<endl;
}