sobota, 1 marca 2014

Fabryka abstrakcyjna (obiektowy)

Fabryka Abstrakcyjna (Abstract Factory)


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