Skupia się na tworzeniu obiektów jednego typu (tej samej rodziny) bez specyfikowania ich konkretnych klas. Umożliwia jednemu obiektowi tworzenie różnych, powiązanych ze sobą podobiektów.
Czas na przykład, który mam nadzieje wyjaśni zasadę działania tego wzorca. Będzie to przykład ilustrujący fabrykę tworzenia przycisków pod różne systemy operacyjne.
1. Na początku tworzymy fabrykę, która posiada metodę statyczną getFactory() i zwraca odpowiedni typ już konkretnej fabryki na podstawie systemu, który zczytuje z pliku. Można implementować poniższy fragment na wiele różnych sposobów. Nie jest powiedziane, że za każdym razem budowa metody ma tak samo wyglądać. Chodzi mi tutaj np. o metodę readFromConfigFile(). W naszym przykładzie informacje o systemie na jakim pracujemy możemy równie dobrze zczytywać w jakiś inny sposób, albo mieć to w jakiś sposób zahardcodowane. Oprócz metody getFactory() mamy również metodę createButton(), którą każda już konkretna fabryka będzie implementowała na swój sposób.
/* Przykład GUIFactory */ abstract class GUIFactory { public static GUIFactory getFactory() { int sys = readFromConfigFile("OS_TYPE"); if (sys == 0) { return new WinFactory(); } else { return new OSXFactory(); } } public abstract Button createButton(); }2. Teraz czas na konkretna fabrykę - WinFactory, która rozszerza klasę abstrakcyjną GUIFactory i zarazem musi oraz implementuje metode createButton() zwracając tym samym już konkretny typ przycisku, a dokładniej pod system Windows.
class WinFactory extends GUIFactory { public Button createButton() { return new WinButton(); } }3. Następnie analogicznie jak powyżej klasa - OSXFactory, która zwraca przycisk pod system OS.
class OSXFactory extends GUIFactory { public Button createButton() { return new OSXButton(); } }
4. Tworzymy klase abstrakcyjną Button. Jedyne, co w niej będziemy przechowywać
to metoda abstrakcyjną paint(), która będzie implementowana już przez konkretne
klasy przycisków pod Windowsa oraz OS-a.
abstract class Button { public abstract void paint(); }5. Klasa WinButton rozszerza klasę Button i implemetuje metode paint(). Na swój sposób klasa ta będzie rysowała przycisk pod system Windows.
class WinButton extends Button { public void paint() { System.out.println("Przycisk WinButton"); } }
6. Analogicznie jak powyżej klasa OSXButton będzie rysowała przycisk
w jakiś inny sposób.
Oczywiście metody paint() w obu powyższych klasach nic nie robią oprócz
wyświetlania jakiegoś komunikatu w konsoli, ale chodzi oczywiście tylko
o zademonstrowanie mechanizmu działania wzorca:-).
class OSXButton extends Button { public void paint() { System.out.println("Przycisk OSXButton"); } }
7. Czas na ostatni fragment kodu - przetestowanie naszych klas.
Najpierw tworzymy obiekt klasy GUIFactory. Metoda getFactory()
była metodą statyczną więc bez problemów możemy ją wykonać nie
mając obiektu klasy GUIFactory. Jak wcześniej napisałem metoda
ta zwraca konkretną fabrykę, na podstawie rodzaju systemu jaki
przeczyta z pliku konfiguracyjnego.
W kolejnej linijce działamy wykorzystując polimorfizm, poniewaz
nasz obiekt factory jest już konkretną implementacją i dlatego
możemy na rzecz tego obiektu wywołać metodę createButton(), która
zwróci nam konkretny typ przycisku.
W następnej linijce możemy wyświetlić nasz przycisk i sprawdzić
czy działa nasz program poprawnie. I tak jak jest napisane
w komentarzu zostanie wyświetlony odpowiedni tekst
"Przycisk WinButton" lub "Przycisk OSXButton". W zależności
od tego jaki typ systemu będzie zdefiniowany w pliku konfiguracyjnym.
public class DekorowaneOknoTest { public static void main(String[] args) { // utwórz dekorowane Okno z poziomymi i pionowymi paskami przewijania Okno dekorowaneOkno = new PoziomePrzewijanieDekorator( new PionowePrzewijanieDekorator(new ZwykłeOkno())); // wypisz opis Okna System.out.println(dekorowaneOkno.pobierzOpis()); } }
Wzorzec Fabryka Abstrakcyjna jest podobnym wzorcem do wzorca Budowniczy
oraz Metoda Wytwórcza i może się z tymi wzorcami mylić. Jaka jest więc
różnica?
Zachęcam do przeczytania postu o budowniczym jeżeli chce się przestudiować
dokładne działanie tego wzorca. Jednak główna różnica polega na tym, iż
w budowniczym mieliśmy w samej klasie PizzaBuilder (klasie abstrakcyjnej
budowniczego) obiekt klasy Pizza (abstrakcyjnej klasy, której obiekty
budowaliśmy). W fabryce nie mamy obiektu klasy, której produkujemy (naszego
przycisku), a jedynie metode getFactory(), która zwraca konkretna fabrykę,
a ta natomiast tworzy dynamicznie obiekt przyciku.
Jakby to przełożyć na budowniczego to w budowniczym mielibyśmy obiekt
abstrakcyjny przycisku i nastepnie byśmy go budowali, tzn. dodawali jakieś
właściwości pod konkretny system. Tak jak było z pizza. Mieliśmy obiekt pizzy
w abstrakcyjnej klasie budowniczego, a w konkretnych tylko budowalismy daną
pizze. Budowaliśmy ciasto, sos, dodatki dla konkretnego rodzaju pizzy.
W przypadku przyciku moglibyśmy "budować" np. tło porzycisku.
W fabryce tego "przycisku" nie mamy, tej "pizzy" nie mamy, jako obiektu jakiejś
klasy abstrakcyjnej tylko dopiero w momencie, gdy wiemy już jaki przycisk
konkretnie stworzyć piszemy odpowiednio :
return new WinButton()lub
return new OSXButton()W przypadku "pizzy" byłoby np.
return new HawaiiPizza().
Mam nadzieje, że pomimo tego, że troche się rozpisałem każdy zrozumie jaka
jest różnica pomiędzy tymi dwoma wzorcami. Zachęcam do przeczytania oddzielnych wpisów na temat budowniczego oraz metody fabrykującej.
Teraz czas na diagram klas tego wzorca.
Brak komentarzy:
Prześlij komentarz