Wzorce projektowe: Fabryka abstrakcyjna(abstract factory) i jej rodzaje

Dzisiaj o fabryce abstrakcyjnej(abstract factory) i jej rodzajach, czyli prosta fabryka(simple factory) oraz metoda fabrykująca(factory method). To będzie długa lekcja bo trochę do omówienia jest, to do dzieła 🙂

Każdemu programiście zdarzyło się utworzyć obiekt klasy w taki sposób:

Ta klasa jest związana z konkretnym typem, a tworzenie czegoś takiego w klasie jest niedopuszczalne, pamiętamy piątą zasadę SOLID dependency inversion, żeby uzależniać klasę od abstrakcji a nie od konkretnego typu.

Lepsze byłoby przypisanie obiektu do jego interfejsu:

A więc wzorzec fabryka i wszelkie jej odmiany służą temu aby nie tworzyć konkretnych typów obiektu klasy tylko abstrakcyjne typy klas. Zamiast podawania słówka new tworzymy metodę, która zwraca abstrakcyjną instancję obiektu klasy.

Fabryka abstrakcyjna służy również temu by klasa, którą tworzymy była otwarta na rozbudowę, ale zamknięta na modyfikacje. Na pewno również spotkałeś się z takim kodem:

Ano spotkaliśmy się również z podobnym kodem w drugiej lekcji zasad SOLID, gdy omawiałem zasadę open-closed, gdy będziemy chcieli dodać inny produkt to będziemy musieli zmodyfikować tę metodę, a wtedy już byśmy złamali zasadę open-closed. Powinno to zostać zahermetyzowane wewnątrz fabryki.

 

Kiedy powinniśmy używać fabryki abstrakcyjnej?

Trzeba zapamiętać, że fabrykę używamy tam gdzie chcemy odciąć się od tworzenia instancji klasy konkretnego typu. 

Może to być spowodowane np wtedy kiedy instancja posiada skomplikowaną logikę tworzenia jej instancji albo np chcemy utworzyć instancję jakieś klasy, ale jej typ będzie zależny od dodatkowych parametrów, wtedy powinniśmy użyć fabryki. Inne powody za użyciem fabryk:

  1. Klasa ma wielki, przeciążony konstruktor z wieloma argumentami – oznaczać to może źle zaprojektowaną klasę.
  2. Utworzenie instancji klasy jest w warunku if- taką logiką należy z całą pewnością zahermetyzować wewnątrz fabryki przy użyciu dziedziczenia i polimorfizmu, o czym już wspominałem zresztą gdy omawiałem drugą zasadę solid.
  3. Jeśli w momencie pisania kodu nie wiesz, której instancji użyć, to też i w tym wypadku trzeba użyć hermetyzacji.

Wiesz, że podczas lekcji o builderze połączyliśmy fabrykę z builderem, a raczej podobny sposób zwracania obiektu, spójrz na klasę Shop w lekcji o builderze, popatrzmy na metodę zwracającą obiekt dachu:

Metody w fabryce wyglądają bardzo podobnie, również zwracają abstrakcyjne typy obiektu, jednak bardziej dokładna koncepcja fabryki jest bardziej skomplikowana, w builderze zrobiłem to po to, żeby nie łamać piątej zasady solid, normalny builder miałby po prostu konkretne typy metod o praktycznych przykładach zastosowania fabryk dalej w lekcji.

 

Prosta fabryka(Simple factory)

Prosta fabryka najlepiej się sprawdza w prostych przykładach, ponieważ to rozwiązanie ma jedną dużą wadę o której zaraz będzie mowa, najczęściej tego rozwiązania używają początkujący programiści, ponieważ ta fabryka jest prosta to zaimplementowania. Poniżej przykład:

Kod jest bardzo prosty. Instancje zwracane są w postaci warstwy abstrakcji.

Tylko właśnie tą jedną poważną wadą, które ma to rozwiązanie jest złamanie zasady open-closed, gdy będziemy chcieli dodać jakiś inny owoc to oprócz klasy, którą będziemy musieli dodać(dodawanie nowych klas nie jest złamaniem zasady solid) będziemy musieli rozbudować naszego switcha co już jest złamaniem zasady open-closed.

Można również zastosować wersje statyczną klasy. Wystarczy oznaczyć metodą jako statyczną słówkiem static. Wtedy nie będzie trzeba za każdym razem tworzyć instancji obiektu. Jednak klasy statyczne oczywiście są trudniejsze do testowania.

 

Metoda fabrykująca (factory method)

Metoda fabrykująca to najczęściej używany rodzaj fabryki, wśród programistów, niesie za sobą wszystkie korzyści używania fabryk oraz spełnia drugą zasadę SOLID open-closed. Dlatego, że tworzenie instancji następuje w klasach potomnych poszczególnych fabryk. Przykład:

Jak widać klasa fabryki jest teraz klasą abstrakcyjną. Logika w switchu została zastąpiona mechanizmem polimorfizmu. Teraz jeśli będziemy chcieli dodać jakąś inną część sklepu do budowy, wystarczy dodać klasę fabryki, nie musimy zmieniać klasy fabryki, czyli jest spełniona zasada open- closed.

Dzięki korzystaniu z klasy abstrakcyjnej jaką jest metoda fabrykująca, możemy posiadać klasy, które będą po niej dziedziczyć i wykorzystywać jej metody, czyli mamy większę możliwości niż w prostej fabryce, która ma jedynie jeden prosty switch.

Jest wiele zalet metody fabrykującej oto najważniejsze z nich:

-spełnia zasadę open-closed, jest łatwiejsza do późniejszych modyfikacji

-główny kod fabryki może być niewidoczny dla programisty np w jakieś bibliotece a i tak będzie mógł ją rozbudowywać dzięki zachowanej zasadzie open-closed

-warstwa abstrakcji, która hermetyzuje skomplikowaną logikę tworzenia instancji obiektu.

-fabryki pochodne są łatwe do testowania, wystarczy wprowadzić interfejs, który będą spełniać.

 

Fabryka abstrakcyjna(abstract factory)

Fabryka abstrakcyjna jako ostatnia z odmian fabryk jest najtrudniejsza do zrozumienia. W fabryce abstrakcyjnej chodzi o tworzenie obiektów spokrewnionych ze sobą. Główna zasada mówi, że fabryka abstrakcyjna ma dostarczyć interfejs do tworzenia rodziny obiektów, najlepiej zobaczyć to na przykładzie, żeby to zrozumieć:

W powyższym przykładzie klasa abstrakcyjna SpaceConstructionFactory jest fabryką definiującą rodzinę obiektów. Za pomocą kompozycji ustaliliśmy, że klasa klienta i klasy pochodne fabryki są od siebie zależne.

Najważniejszym elementem fabryki abstrakcyjnej jest klasa klienta. Za pomocą kompozycji tworzymy klasę zawierającą odpowiednią fabrykę.

 

Jakie korzyści dają wzorce fabryki i jej odmiany?

Zalety

Dzięki fabryce uzyskujemy warstwę abstrakcyjną, która jest odpowidzalna za tworzenie obiektów powiązanych jedynie wspólnym interfejsem. Najważniejsze zalety użycia fabryk:

  1. Zachowuje zasadę odwrócenia zależności(Dependency inversion) klasy, które łączy jedynie wspólny interfejs. Takie klasy są łatwiejsze w testowaniu oraz mniejsza szansa o błedy, gdy będziemy modyfikować te klasy, zamiast tworzyć nowe klasy np new Bread() to tworzymy metodę fabrykującą zwracającą obiekty interfejsu IProducts.
  2. Dostarcza warstwę abstrakcji dzięki czemu hermetyzuje odpowiednią logikę wewnątrz fabryki. Hermetyzacja jako podstawa programowania obiektowego również niesie za sobą inne korzyści np uproszczenie kodu, ukrycie logiki między warstwami abstrakcji.
  3. Spełnia zasadę otwarty – zamknięty(Open- closed) – rozbudowywując klasę nie mamy obawy, że coś zepsujemy.
  4. Kod jest współdzielony, czyli reużywalny raz utworzony może zostać wykorzystany w innym miejscu systemu.

 

Jakie są wady fabryki jeśli źle jej użyje?

Tak jak wspominałem wcześniej, wszystkie wzorce trzeba używać z rozsądkiem, odwołuje się do zdania Uncle Boba z poprzedniej lekcji, jak nie pamiętasz to wróć do niej.

Wady

  1. Tworzenie fabryki „na wszelkie wypadek”, czyli ktoś ma przeczucie, że może będzie chciał zainicjalizować obiekt w inny sposób, to jest złamania zasdy YAGNI(You don’t gonna need it)
  2. Nie tworzyć fabryki jeśli w jej wnętrzu jest pojedynczy wielki switch

 

Ogólna struktura wzorca Fabryki abstrakcyjnej

Zobaczmy jeszcze jak wygląda diagram UML wzorca fabryki:

Scheme of Abstract Factory

Widać na nim, że Fabryka abstrakcyjna bardzo często wykorzystuje metody fabrykujące.

 

Powiązania z innymi wzorcami

  • Często różne wzorce wykorzystują Metodę fabrykującą(Factory method) np budowniczego(builder) lub prototyp(prototype) wtedy kiedy potrzebna jest elastyczność.
  • Klasy Fabryki abstrakcyjnej są często zaimplementowane przy użyciu metody fabrykującej, ale mogą być też zaimplementowane przy użyciu prototypu
  • Wzorce Fabryka abstrakcyjna(Abstract Factory), Budowniczy(Builder)Prototyp(Prototype) mogą być zaimplementowane jako Singletony
  • Często implementacja Budowniczego(Buildera) jest połączona z Fabryką abstrakcyjną(Abstract factory), żeby zachować elastyczność i nie tworzyć typów klas konkretnych.

 

Podsumowanie wzorca fabryki

Wzorzec fabryki abstrakcyjnej często jest uważany za jeden z trudniejszych(jeśli nie najtrudniejszy) wzorzec projektowy. Ta komplikacja wynika z tego, że jest wiele możliwości implementacji tego wzorca w tej lekcji pokazałem tylko małą ich część. Można ten wzorzec modyfikować i łączyć z innymi wzorcami. Poniżej małe podsumowanie odmian wzorców fabryki:

 

Prosta fabryka

  • najprostsza możliwość oddzielenia implementacji od interfejsu.
  • brak dziedziczeniabrak kompozycji.
  • brak klasy klienta.
  • ciężka do testowania
  • złamanie zasady open-closed

 

Metoda wytwórcza

  • spełnia zasadę open-closed
  • zawsze występuje mechanizm dzidziczenia
  • brak kompozycji
  • skomplikowana logika jest zahermetyzowana
  • łatwa do testowania

 

Fabryka abstrakcyjna

  • tworzy rodziny obiektów w jakiś sposób powiązanych ze sobą
  • występuje mechanizm dziedziczenia
  • występuje mechanizm kompozycji
  • występuje dedykowana klasa klienta
  • łamie zasadę open-closed

 

Podsumowanie

I to wszystko na temat wzorca Fabryka abstrakcyjna🙂

Ale długi ten artykuł wyszedł 🙂 . Mam nadzieję, że trochę ci rozjaśnił działanie wzorca fabryki 🙂

W następnym artykule, będzie mowa o wzorcu Prototyp(Prototype).

Standardowo, przypominam o newsletterze, którym wysyłam powiadomienia o nowych wpisach oraz dodatkowe informacje na temat, ogółem mówiąc, świecie IT.

KONIECZNIE dołącz do społeczności DevmanCommunity na fb, części społeczności jest w jednym miejscu 🙂

-strona na fb: Devman.pl-Sławomir Kowalski

-grupa na fb: DevmanCommunity

Pytaj, komentuj pod spodem na końcu wpisu, podziel się nim, oceń go, rób co wolisz🙂

 
Jeśli ten wpis ci się przydał podziel się nim ze swoimi znajomymi :)

Post a comment

avatar
  Subscribe  
Notify about