Wzorce projektowe

Transkrypt

Wzorce projektowe
Wzorce projektowe
Informatyka, II stopień,
studia stacjonarne
wykład 3
Fabryka Abstrakcyjna
(ang. Abstract Factory)
Definicja:
• Jeden z kreacyjnych wzorców projektowych (obiektowy),
którego celem jest dostarczenie interfejsu do tworzenia
różnych obiektów jednego typu (tej samej rodziny) bez
specyfikowania ich konkretnych klas.
• Wzorzec umożliwia jednemu obiektowi tworzenie różnych,
powiązanych ze sobą, reprezentacji podobiektów
określając ich typy podczas działania programu.
Fabryka Abstrakcyjna
Cel użycia:
• Stosuje się ten wzorzec, kiedy zachodzi potrzeba
skoordynowania procesów tworzenia rodzin obiektów.
• Pozwala na wyodrębnienie reguł dotyczących tworzenia
obiektów z obiektów użytkownika, które będą
wykorzystywać tworzone obiekty.
• Ułatwia przystosowywanie kodu źródłowego do pracy w
zróżnicowanych środowiskach. System tworzy nową
unikatową konkretną fabrykę (która z kolei tworzy nowy
unikatowy produkt) dla każdego środowiska. Ze względu na
korzystanie w kodzie wyłącznie z interfejsów, gwarantuje
właściwą współpracę z konkretnymi środowiskami.
Fabryka Abstrakcyjna
• Fabryka abstrakcyjna różni się od Budowniczego, tym,
że kładzie nacisk na tworzenie produktów z konkretnej
rodziny, a Budowniczy kładzie nacisk na sposób tworzenia
obiektów.
Przykład użycia:
• Aplikacja kliencka łącząca się ze zdalnym serwerem.
Aplikacja ma być przenośna.
Jednym z rozwiązań takiego problemu jest stworzenie
fabryki, która będzie tworzyła odpowiednie obiekty w
zależności od tego na jakiej platformie się znajduje.
Fabryka Abstrakcyjna
• Na diagramie klas: wzorzec zbudowany jest z kilku
podstawowych klas.
• Klasa Fabryka abstrakcyjna deklaruje abstrakcyjny interfejs
umożliwiający tworzenie produktów.
• Interfejs ten jest implementowany w Fabrykach
konkretnych, które odpowiedzialne są za tworzenie
konkretnych produktów.
• Każda fabryka konkretnego produktu posiada także metodę
wytwórczą tego produktu.
Fabryka Abstrakcyjna
class AbstractProductA
{
public:
virtual ~AbstractProductA()=0;
virtual void operation()=0;
protected:
AbstractProductA();
};
class AbstractProductB
{
public:
virtual ~AbstractProductB()=0;
virtual void operation()=0;
protected:
AbstractProductB();
};
class ProductA1:public AbstractProductA
{
public:
ProductA1();
virtual void operation();
virtual ~ProductA1();
};
class ProductB1:public AbstractProductB
{
public:
ProductB1();
virtual void operation();
virtual ~ProductB1();
};
class ProductA2:public AbstractProductA
{
public:
ProductA2();
virtual void operation();
virtual ~ProductA2();
};
class ProductB2:public AbstractProductB
{
public:
ProductB2();
virtual void operation();
virtual ~ProductB2();
};
class AbstractFactory
{
public:
virtual ~AbstractFactory()=0;
virtual AbstractProductA* CreateProductA()=0;
virtual AbstractProductB* CreateProductB()=0;
protected:
AbstractFactory();
};
class ConcreteFactory1:public AbstractFactory
{
public:
ConcreteFactory1();
~ConcreteFactory1();
virtual AbstractProductA* CreateProductA();
virtual AbstractProductB* CreateProductB();
};
class ConcreteFactory2:public AbstractFactory
{
public:
ConcreteFactory2();
~ConcreteFactory2();
virtual AbstractProductA* CreateProductA();
virtual AbstractProductB* CreateProductB();
};
AbstractProductA::AbstractProductA()
{
cout <<"AbstractProductA..." << endl;
}
AbstractProductB::AbstractProductB()
{
cout <<"AbstractProductB..." << endl;
}
AbstractProductA::~AbstractProductA()
{
cout <<"~AbstractProductA..." << endl;
}
AbstractProductB::~AbstractProductB()
{
cout <<"~AbstractProductB..." << endl;
}
ProductA1::ProductA1()
{
cout <<"ProductA1..." << endl;
}
ProductB1::ProductB1()
{
cout <<"ProductB1..." << endl;
}
ProductA1::~ProductA1()
{
cout <<"~ProductA1..." << endl;
}
ProductB1::~ProductB1()
{
cout <<"~ProductB1..." << endl;
}
void ProductA1::operation()
{}
void ProductB1::operation()
{}
ProductA2::ProductA2()
{
cout <<"ProductA2..." << endl;
}
ProductB2::ProductB2()
{
cout <<"ProductB2..." << endl;
}
ProductA2::~ProductA2()
{
cout <<"~ProductA2..." << endl;
}
ProductB2::~ProductB2()
{
cout <<"~ProductB2..." << endl;
}
void ProductA2::operation()
{}
void ProductB2::operation()
{}
AbstractFactory::AbstractFactory()
{
cout <<"AbstractFactory..." << endl;
}
ConcreteFactory2::~ConcreteFactory2()
{
cout <<"~ConcreteFactory2..." << endl;
}
AbstractFactory::~AbstractFactory()
{
cout <<"~AbstractFactory..." << endl;
}
AbstractProductA* ConcreteFactory2::CreateProductA()
{
return new ProductA2();
}
ConcreteFactory1::ConcreteFactory1()
{
cout <<"ConcreteFactory1..." << endl;
}
AbstractProductB* ConcreteFactory2::CreateProductB()
{
return new ProductB2();
}
ConcreteFactory1::~ConcreteFactory1()
{
cout <<"~ConcreteFactory1..." << endl;
}
int main()
{
AbstractFactory* fa1 = new ConcreteFactory1();
AbstractProductA* a1 = fa1->CreateProductA();
AbstractProductB* b1 = fa1->CreateProductB();
AbstractProductA* ConcreteFactory1::CreateProductA()
{
return new ProductA1();
}
AbstractFactory* fa2 = new ConcreteFactory2();
AbstractProductA* a2 = fa2->CreateProductA();
AbstractProductB* b2 = fa2->CreateProductB();
AbstractProductB* ConcreteFactory1::CreateProductB()
{
return new ProductB1();
}
ConcreteFactory2::ConcreteFactory2()
{
cout <<"ConcreteFactory2..." << endl;
}
delete
delete
delete
delete
delete
delete
return
}
fa1;
a1;
b1;
fa2;
a2;
b2;
0;
Fabryka Abstrakcyjna
Zalety:
• możliwość ukrycia szczegółów implementacyjnych klas
reprezentujących konkretny produkt - klient widzi tylko
interfejs. Ukryciu ulegają także nazwy tych klas, co nie
wymusza ich zapamiętywania i odizolowuje klienta od
problemu określenia, do której klasy należy obiekt.
• możliwość całkowitego ukrycia implementacji obiektów
przed klientem. Klient widzi tylko interfejs i nie ma
możliwości zajrzenia do kodu oraz to, że wymuszana jest
spójność produktów.
Fabryka Abstrakcyjna
Wady:
• trudność rozszerzania rodziny obiektów o nowe podobiekty.
Wymusza to modyfikację klasy fabryki abstrakcyjnej oraz
wszystkich obiektów, które są tworzone przez nią.
• Gdy dany produkt nie realizuje powierzonych mu zadań
zgodnie z oczekiwaniami, najczęściej należy dokonać zmiany
interfejsu abstrakcyjnego produktu – może to być dosyć
trudne, ponieważ wymaga zmodyfikowania definicji
wszystkich konkretnych produktów.
Prototyp (ang. Prototype)
Definicja:
• Jeden z kreacyjnych wzorców projektowych. Umożliwia
tworzenie obiektów danej klasy (lub klas) z wykorzystaniem
już istniejącego obiektu, zwanego prototypem.
• Głównym celem tego wzorca jest uniezależnienie systemu
od sposobu w jaki są tworzone, składane i reprezentowane
w nim produkty.
Prototyp
• Używany głównie w celu konieczności utworzenia prawdziwej
kopii (ang. truecopy) innej instancji w czasie wykonywania
programu (przez dynamiczne ładowanie) – potrzeba użycia
metody clone().
• Prawdziwa kopia to taka, w której skopiowany obiekt ma
wszystkie swe pola identyczne z pierwowzorem.
• Gdy używa się operatora new, wówczas pola obiektu mają
wartości początkowe.
Np. w systemie do przeprowadzania transakcji bankowych potrzebna jest taka kopia obiektu, która przechowa dane
konta, wykona transakcje na tej kopii i zamieni tę kopię z
oryginałem (potrzebna jest raczej metoda clone() niż operator
new).
Prototyp
• Wzorzec Prototyp określa rodzaj obiektów do tworzenia za
pomocą prototypowej instancji. Prototypy nowych produktów
są często budowane przed pełną produkcją.
• Prototyp - deklaruje interfejs klonowania się.
• PrototypKonkretny - implementuje operację klonowania się.
• Klient - tworzy nowy obiekt, prosząc prototyp o sklonowanie
się. Współpraca: Klient prosi Prototyp o sklonowanie się.
Prototyp
class Prototype
{
protected:
Prototype();
public:
virtual Prototype* Clone() const=0;
virtual ~Prototype();
};
class ConcretePrototype1:public Prototype
{
public:
ConcretePrototype1();
~ConcretePrototype1();
ConcretePrototype1(const ConcretePrototype1&);
virtual Prototype* Clone() const;
};
class ConcretePrototype2:public Prototype
{
public:
ConcretePrototype2();
~ConcretePrototype2();
ConcretePrototype2(const ConcretePrototype2&);
virtual Prototype* Clone() const;
};
Prototype::Prototype()
{
cout<<"Prototype"<<endl;
}
Prototype::~Prototype()
{
cout<<"~Prototype"<<endl;
}
ConcretePrototype1::ConcretePrototype1(const
ConcretePrototype1& cp)
{
cout<<"ConcretePrototype1 copy"<<endl;
}
Prototype* ConcretePrototype1::Clone() const
{
return new ConcretePrototype1(*this);
}
ConcretePrototype2::ConcretePrototype2()
{
cout<<"ConcretePrototype2"<<endl;
}
ConcretePrototype2::~ConcretePrototype2()
{
cout<<"~ConcretePrototype2"<<endl;
}
ConcretePrototype2::ConcretePrototype2(const
ConcretePrototype2& cp)
{
cout<<"ConcretePrototype2 copy"<<endl;
}
Prototype* ConcretePrototype2::Clone() const
{
return new ConcretePrototype2(*this);
}
int main()
{
Prototype* p1 = new ConcretePrototype1();
Prototype* p2 = p1->Clone();
ConcretePrototype1::ConcretePrototype1()
{
cout<<"ConcretePrototype1"<<endl;
}
Prototype* p3 = new ConcretePrototype2();
Prototype* p4 = p3->Clone();
delete p1;
delete p2;
ConcretePrototype1::~ConcretePrototype1()
{
cout<<"~ConcretePrototype1"<<endl;
}
delete p3;
delete p4;
return 0;
}
Prototyp
Zalety:
• Prototyp może skrócić czas tworzenia obiektów.
• Można zainstalować nowy konkretny produkt w ramach wytwórni
przez proste przekazanie tej wytwórni prototypu (już w czasie
wykonania programu). Usuwanie produktów jest równie łatwe.
Wady:
• Konieczność interpretowania metody clone() – niekiedy może być
dość trudne.
• Należy rozważyć problem „głębokiej” lub „płytkiej” kopii,
tzn. czy kopia powinna się sprowadzać do referencji, czy należy
klonować cały wskazany obiekt
• Metoda klonująca powinna niekiedy działać jak konstruktor –
inicjalizować pewne pola wartościami domyślnymi, np. klon pola listy
zwykle nie może się znaleźć na liście.

Podobne dokumenty