2010 02 Fabryki obiektów [Programowanie C C ]


Programowanie C++
Fabryki obiektów
Techniki opisane w tym artykule pozwalają tworzyć obiekty na
podstawie identyfikatorów dostarczanych w czasie działania programu,
co jest wygodniejsze niż podawanie typów w formie zrozumiałej dla
kompilatora.
tworzyć obiekty na podstawie identyfikato-
Dowiesz się: Powinieneś wiedzieć: ra, rolę tę pełni funkcja createObj. Po utwo-
" Jak tworzyć obiekty w C++; " Jak pisać proste programy w C++; rzeniu obiektu możemy wczytać składowe,
" Co to jest wzorzec fabryki. " Co to jest dziedziczenie i funkcje wirtualne. wykorzystując mechanizm funkcji wirtual-
nych, wołając metodę read dla utworzone-
go obiektu.
Przy zapisie obiektu do strumienia wyj-
ściowego (na przykład do pliku) nie potrze-
bujemy dodatkowych mechanizmów, któ-
Funkcje fabryczne re dostarczą identyfikator dla danego typu,
Przykład wykorzystania fabryki obiektów ponieważ możemy wykorzystać mechanizm
Poziom został pokazany na Listingu 2, gdzie poka- funkcji wirtualnych. Posiadając wskaznik lub
trudności zano funkcję create tworzącą obiekty klas referencję na klasę bazową, wołamy meto-
na podstawie danych zapisanych w strumie- dę write, która zapisuje identyfikator klasy
niu wejściowym, na przykład w pliku. Funk- konkretnej oraz jej składowe. Odczyt obiek-
cja ta dostarcza obiekt odpowiedniego typu tu jest o wiele bardziej złożony niż zapis ze
abryka obiektów jest klasą, której konkretnego dla hierarchii Figure. Metody względu na to, że zapis posługuje się istnie-
obiekty pośredniczą przy tworze- zapisu (write) oraz odczytu (read) są do- jącymi obiektami, natomiast odczyt musi je
Fniu innych obiektów. Dostarcza- starczane przez każdą z klas konkretnych. tworzyć.
na informacja jednoznacznie identyfiku- Jeżeli chcemy obiekt utworzyć (odczytać),
je konkretny typ, znany w momencie kom- to typ obiektu jest dostarczany w czasie dzia- Fabryka skalowalna
pilacji, ale nie jest to literał, więc informa- łania, jest on wyznaczany przez identyfika- Funkcja createObj jest prostą fabryką obiek-
cja o typie jest nieodpowiednia dla kom- tor dostarczany przez strumień. Ponieważ tów, pozwala ona tworzyć obiekty na podsta-
pilatora, na przykład jest to napis lub inny tak dostarczany identyfikator nie jest ak- wie informacji, która jest dostarczana w cza-
identyfikator. Fabryka ukrywa przed użyt- ceptowany jako argument dla operacji new, sie działania, ale ma wiele wad: mapowanie
kownikiem mechanizm zamiany identyfi- należy wykorzystać fabrykę, która pozwoli identyfikatora na typ za pomocą instrukcji
katora na literał dostarczany do operato-
ra new, upraszczając tworzenie obiektów
Listing 1. Podczas tworzenia obiektu należy podać typ w odpowiedniej formie
(patrz Rysunek 1).
W języku C++ podczas tworzenia obiektu class Bazowa { /* ... */ }; //przykładowa definicja typu
należy podać konkretny typ w formie zrozu- class KonkretnaA : public Bazowa { /* ... */ };
miałej dla kompilatora (patrz Listing 1). Nie Bazowa* p = new KonkretnaA; //przy tworzeniu trzeba podać konkretny typ
możemy posłużyć się mechanizmem funkcji //nazwa typu musi być zrozumiała dla kompilatora
wirtualnych, nie możemy także przekazać
identyfikatora typu w czasie działania, argu-
mentem operatora new może być tylko literał
oznaczający typ znany w momencie kompila-
Szybki start
cji. Po utworzeniu obiektu można się do nie- Aby uruchomić przedstawione przykłady, należy mieć dostęp do kompilatora C++ oraz
edytora tekstu. Niektóre przykłady korzystają z udogodnień dostarczanych przez bi-
go odwoływać poprzez wskaznik lub referen-
bliotekę boost::mpl, warunkiem ich uruchomienia jest instalacja bibliotek boost (w wer-
cję na klasę bazową, ale przy tworzeniu nale-
sji 1.36 lub nowszej) Na wydrukach pominięto dołączanie odpowiednich nagłówków
ży podać rzeczywisty typ obiektu, nie można
oraz udostępnianie przestrzeni nazw, pełne zródła dołączono jako materiały pomoc-
użyć mechanizmu póznego wiązania (funk-
nicze.
cji wirtualnych).
30 02/2010
Fabryki obiektów
switch sprawia, że funkcja ta jest zależna od nie moduł ten może zawierać kod rejestrują- zanie bezpośrednie (wybór typu w zależno-
wszystkich klas w hierarchii, jeżeli będzie cy dany typ w fabryce, tak jak pokazano na ści od identyfikatora za pomocą switch lub
dodawana lub usuwana jakaś klasa, to mody- Listingu 4. łańcucha if ... else), ponieważ wymaga prze-
fikacji będzie musiał podlegać także kod fa- Fabryka skalowalna jest bardziej elastycz- chowywania kolekcji wiążącej identyfikator z
bryki; brak kontroli przy wiązaniu identyfi- na, ale też bardziej kosztowna niż rozwią- funkcją tworzącą. Obiekt jest tworzony za po-
katora z typem sprawia, że musimy zapew-
nić, aby przy odczycie obiektu korzystać z
Listing 2. Przykład, w którym uzasadnione jest wykorzystanie fabryki
tego samego identyfikatora co przy zapisie.
Poza tym, zestaw identyfikatorów jest zaso- class Figure { //klasa bazowa
bem globalnym, przy modyfikowaniu zbio- public:
ru klas w danej hierarchii musi on podlegać enum Type { SQUARE, CIRCLE, /* ... */ };//identyfikatory klas konkretnych
modyfikacjom. virtual bool write(ostream& os) const = 0;//zapisuje obiekt
Fabryka skalowalna, przedstawiona na virtual bool read(istream& is) = 0;//odczytuje obiekt
Listingu 3, umożliwia tworzenie obiektów };
na podstawie identyfikatorów, wprowadza class Square : public Figure {//jedna z klas konkretnych
mniejszą liczbę zależności w porównaniu public:
z poprzednio omówionym rozwiązaniem, bool zapisz(ostream& os) {//zapisuje kwadraty
ponieważ jest ona zależna tylko od klasy os << KWADRAT;//zapisuje identyfikator typu
bazowej, a nie od wszystkich klas konkret- //zapisuje poszczególne składowe
nych. Mniejsza liczba zależności wynika z }
zastosowania wskaznika na funkcję two- bool read(istream& is) {//odczytuje obiekt, zakładając, że jest to kwadrat
rzącą obiekty danej klasy konkretnej oraz //odczytuje poszczególne składowe
przez użycie dynamicznej struktury prze- }
chowującej mapowanie pomiędzy identy- };
fikatorem a typem. //pozostałe klasy konkretne także dostarczają metody odczytu i zapisu
Klasa konkretna woła metodę registerFig, //...
przekazując swój identyfikator oraz funkcję Figure* createObj(istream& is) {//funkcja pełni rolę fabryki
tworzącą obiekty danej klasy. Metoda ta doda- Figure::Type type;
je element do kolekcji, można więc elastycznie is >> type; //odczytuje identyfikator typu
modyfikować zestaw klas, których obiekty bę- Figure* obj;
dą tworzone przez fabrykę. Jeżeli chcemy usu- switch(type) { //zapewnia mapowanie pomiędzy identyfikatorem a typem
wać wpisy, to należy zaimplementować meto- case SQUARE://w formie zrozumiałej dla kompilatora
dę unregister, która będzie usuwała elemen- return new Square(); //tworzy odpowiedni obiekt
ty z kolekcji. case CIRCLE: /* ... */
Tworzenie obiektów odbywa się w meto- }
dzie create, która wyszukuje funkcję two- }
rzącą dla danego identyfikatora. Jeżeli taka //tworzy obiekt na podstawie identyfikatora i odczytuje jego składowe
funkcja zostanie znaleziona, to jest ona woła- Figure* create(istream& is) {
na (patrz Listing 3), a obiekt utworzonej kla- Figure* obj = createObj(is); //tworzy obiekt odpowiedniego typu
sy jest zwracany. obj->read(is);//obiekt istnieje, może wykorzystać funkcje wirtualne
Klasa konkretna musi dostarczyć funkcję }
tworzącą obiekty, funkcja ta (patrz Listing 4)
Listing 3. Fabryka skalowalna
może być umieszczona w module zawierają-
cym implementację klasy konkretnej, podob- class FigFactory {
public:
typedef Figure* (*CreateFig)(); //wskaznik na funkcję tworzącą obiekt
//rejestruje nowy typ
void registerFig(int id, CreateFig fun) {
creators_.insert( value_type(id, fun) ); //dodaje do kolekcji
}
//tworzy obiekt na podstawie identyfikatora
klient
fabryka
Figure* create(int id) { //tworzy obiekt danego typu
Creators::const_iterator i = creators_.find(id);
if(i ! = creators_.end() ) //jeżeli znalazł odpowiedni wpis
utwórz(identyfikator)
obiekt
return (i->second)(); //woła metodę fabryczną
return 0L; //zwraca pusty wskaznik, gdy nieznany identyfikator
}
private:
typedef std::map Creators;
Creators creators_;//przechowuje powiązania pomiędzy identyfikatorem a funkcją
tworzącą
};
Rysunek 1. Tworzenie obiektów przez fabrykę
02/2010 www.sdjournal.org 31
Programowanie C++
średnictwem tej funkcji, więc tworzenie trwa Aby zaimplementować w pełni funkcjo- sze, problem dostarczania odpowiedniego
dłużej (jeden skok więcej w porównaniu z nalną fabrykę obiektów, należy uwzględ- obiektu fabryki wymaganego przy rejestra-
metodą bezpośrednią). nić dodatkowe zagadnienia. Po pierw- cji klas (na wydruku 4 została użyta funk-
cja getFactory). Często stosowanym roz-
wiązaniem jest singleton. Po drugie, należy
Listing 4. Przykład funkcji tworzącej i rejestracji typu w fabryce
zarządzać czasem życia powołanych obiek-
Figure* CreateSquare() { //funkcja tworząca dla typu konkretnego tów, fabryka tworzy obiekty na stercie, ale
return new Square(); kto ma je zwalniać? W tym celu warto po-
}; służyć się sprytnymi wskaznikami (patrz
FigFactory& factory = getFactory();//pobiera obiekt fabryki SDJ 11/2009). Kolejnym zadaniem jest
factory.registerFig(SQUARE, CreateSquare); //rejestruje się w fabryce wiązanie identyfikatora z typem, aby wy-
kluczyć możliwości pomyłek. Można od-
Listing 5. Fabryka skalowalna zarządzająca identyfikatorami
powiedzialność dostarczania identyfika-
typedef shared_ptr
PFigure; //sprytny wskaznik torów przenieść na fabrykę, obiekt ten ma
class FigFactory { może tworzyć unikalne identyfikatory, zaś
public: klasy, które są rejestrowane w fabryce, mo-
gą ten identyfikator przechowywać w skła-
typedef PFigure (*CreateFig)(); //wskaznik na funkcję tworzącą obiekt dowej statycznej (patrz Listing 5). Obiekty
int registerFig(CreateFig fun) {//zwraca id zarejestrowanego typu klas będą wykorzystywały ten identyfika-
creators_.insert( value_type( currentId_, fun) ); tor podczas zapisu, natomiast fabryka wy-
return currentId_++; //zwraca identyfikator korzystuje go podczas odczytu.
} Inną możliwością jest generowanie identy-
PFigure create(int id); //tworzy obiekt danego typu (patrz Listing 3) fikatora przez mechanizmy kompilatora, na
przykład używając struktury type_id, wtedy
private: nie trzeba zarządzać nim w fabryce.
int currentId_; //kolejny wolny identyfikator Inicjacja fabryki skalowalnej (rejestracja
//składowe związane z funkcjami tworzącymi typów) może być uproszczona, jeżeli będzie
}; wykorzystywana kolekcja typów z biblioteki
FigFactory& factory = FigFactory::getInstance();//singleton boost::mpl (patrz SDJ 12/2009) oraz algoryt-
Square::id_ = factory.registerFig(CreateSquare); //ustawia identyfikator my, które na kolekcji operują( patrz Listing
6). Funkcja tworząca może być metodą sta-
Listing 6. Rejestracja klas w fabryce skalowalnej przez algorytm biblioteki boost::mpl
tyczną klasy konkretnej, wtedy nie musi za-
class Square : public Figure { wierać nazwy tworzonej klasy. Takie rozwią-
public: zanie prezentuje Listing 6.
//każda klasa konkretna dostarcza metodę statyczną create Podsumowanie
static Figure* create() { return new Square; } Opisany sposób tworzenia obiektów na pod-
}; stawie identyfikatora dostarczanego pod-
czas działania programu jest jednym z wzor-
struct RegisterFigure { //szablon użyty do rejestracji ców kreacyjnych, termin został zapropono-
template void operator()(T) { wany przez bandę czworga (Gamma, Helm,
T::id_ = Factory::getInstance().registerFig( T::create ); Johnson, Vlissides) w książce Wzorce projek-
} towe. Inne udogodnienia dotyczące tworze-
}; nia obiektów, takie jak fabryki prototypów
typedef mpl::vector Types; //kolekcja typów dla klas konkretnych i fabryki abstrakcyjne, są tematem jednego z
mpl::for_each< Types > ( RegisterFigure() ); //woła w czasie wykonania operację dla kolejnych artykułów.
każdego typu
W Sieci
" http://www.boost.org  dokumentacja bibliotek boost;
" http://www.open-std.org  dokumenty opisujące nowy standard C++.
R0BERT N0WAK
Więcej w książce
Adiunkt w Zakładzie Sztucznej Inteligencji Insty-
Zagadnienia dotyczące współcześnie stosowanych technik w języku C++, wzorce projekto-
tutu Systemów Elektronicznych Politechniki War-
we, programowanie generyczne, prawidłowe zarządzanie zasobami przy stosowaniu wy-
szawskiej, zainteresowany tworzeniem aplika-
jątków, programowanie wielowątkowe, ilustrowane przykładami stosowanymi w bibliotece
cji dla biologii i medycyny, programuje w C++ od
standardowej i bibliotekach boost, zostały opisane w książce Średnio zaawansowane progra-
ponad 10 lat.
mowanie w C++, która ukaże się niebawem.
Kontakt z autorem:rno@o2.pl
32 02/2010


Wyszukiwarka

Podobne podstrony:
2010 03 Tworzenie kopii obiektów [Programowanie C C ]
Myslenie obiektowe w programowaniu Wydanie III
program seminarium 2010 03 11
C Programowanie zorientowane obiektowo Vademecum profesjonalisty
Programowanie Obiektowe Ćwiczenia 5
program egzaminów na uprawnienia budowlane styczeń 2010
[C ]Rataj Podstawy programowania obiektowego
Program nauczania KO IV VI SP 2010
Program ochrony zasobów genetycznych koni rasy wielkopolskiej 2010
Programowanie Obiektowe W Visual Basic Net Dla Ka dego
JavaScript Programowanie obiektowe
Programowanie obiektowe pojęcia

więcej podobnych podstron