Programowanie bez fabryki. Po co używać wzorca fabryki?

Podobne dokumenty
Projektowanie obiektowe. Roman Simiński Wzorce projektowe Wybrane wzorce strukturalne

Różne odmiany budowniczego. Po co używać wzorca budowniczy? Kiedy używać budowniczego

PHP 5 język obiektowy


Obiekt klasy jest definiowany poprzez jej składniki. Składnikami są różne zmienne oraz funkcje. Składniki opisują rzeczywisty stan obiektu.

Czym są właściwości. Poprawne projektowanie klas

Informacje ogólne. Karol Trybulec p-programowanie.pl 1. 2 // cialo klasy. class osoba { string imie; string nazwisko; int wiek; int wzrost;

Wzorce projektowe. dr inż. Marcin Pietroo

Singleton. Cel: Przykład: Zastosowanie: Zapewnienie, że klasa ma tylko jedną instancję i dostarczenie globalnego dostępu do niej.

Abstract Factory (fabryka abstrakcyjna)

Programowanie obiektowe

Dokumentacja do API Javy.

Zaawansowane programowanie w C++ (PCP)

Projektowanie obiektowe oprogramowania Wykład 4 wzorce projektowe cz.i. wzorce podstawowe i kreacyjne Wiktor Zychla 2017

Programowanie obiektowe

C++ Przeładowanie operatorów i wzorce w klasach

Projektowanie obiektowe Wzorce projektowe

Konstruktory. Streszczenie Celem wykładu jest zaprezentowanie konstruktorów w Javie, syntaktyki oraz zalet ich stosowania. Czas wykładu 45 minut.

Metodyki zwinne wytwarzania oprogramowania

Diagram klas UML jest statycznym diagramem, przedstawiającym strukturę aplikacji bądź systemu w paradygmacie programowania obiektowego.

Programowanie obiektowe i zdarzeniowe

Klasy abstrakcyjne i interfejsy

Programowanie Zespołowe

Wzorce Strukturalne. Adapter: opis. Tomasz Borzyszkowski

Programowanie obiektowe

Podczas dziedziczenia obiekt klasy pochodnej może być wskazywany przez wskaźnik typu klasy bazowej.

JAVA W SUPER EXPRESOWEJ PIGUŁCE

Wstęp [2/2] Wbrew częstemu przekonaniu, nie są one gotowymi rozwiązaniami, to tylko półprodukty rozwiązania.

Omówienie wzorców wykorzystywanych w Prism 5.0. Dominika Różycka

Programowanie obiektowe - 1.

Zaawansowane programowanie obiektowe - wykład 5

Metody Metody, parametry, zwracanie wartości

Aplikacje w środowisku Java

Wzorce projektowe. dr inż. Marcin Pietroo

Wprowadzenie do programowania aplikacji mobilnych

Klasa jest nowym typem danych zdefiniowanym przez użytkownika. Najprostsza klasa jest po prostu strukturą, np

Problemy projektowania obiektowego. Czy podobne problemy można rozwiązywac w podobny sposób?

Metodyki zwinne wytwarzania oprogramowania

Szablony funkcji i klas (templates)

WSNHiD, Programowanie 2 Lab. 2 Język Java struktura programu, dziedziczenie, abstrakcja, polimorfizm, interfejsy

Plik pobrano z Tytuł: Wzorce projektowe, cz. 2 Strategy Ostatnia aktualizacja:

Klasy. dr Anna Łazińska, WMiI UŁ Podstawy języka Java 1 / 13

Wzorce projektowe Michał Węgorek

Laboratorium 6 DIAGRAM KLAS (Class Diagram)

Klasy abstrakcyjne, interfejsy i polimorfizm

Zadanie polega na stworzeniu bazy danych w pamięci zapewniającej efektywny dostęp do danych baza osób.

1. Które składowe klasa posiada zawsze, niezależnie od tego czy je zdefiniujemy, czy nie?

Języki i techniki programowania Ćwiczenia 2

Programowanie obiektowe

Dariusz Brzeziński. Politechnika Poznańska, Instytut Informatyki

Techniki programowania INP001002Wl rok akademicki 2018/19 semestr letni. Wykład 3. Karol Tarnowski A-1 p.

Wprowadzenie niektórych zagadnień OOP oraz wzorce operacyjne

Dziedziczenie. Tomasz Borzyszkowski

Wykład 8: klasy cz. 4

Podstawy Programowania Obiektowego

Programowanie współbieżne Wykład 8 Podstawy programowania obiektowego. Iwona Kochaoska

Listy powiązane zorientowane obiektowo

Języki i techniki programowania Ćwiczenia 4 Wzorce

TEMAT : KLASY DZIEDZICZENIE

Programowanie obiektowe

Dzisiejszy wykład. Wzorce projektowe. Visitor Client-Server Factory Singleton

Iteracyjno-rozwojowy proces tworzenia oprogramowania Wykład 3 część 1

Programowanie obiektowe

Projektowanie obiektowe oprogramowania Wykład 4 wzorce projektowe cz.i. wzorce podstawowe i kreacyjne Wiktor Zychla 2015

Aplikacje w środowisku Java

Technologie obiektowe

Język JAVA podstawy. wykład 2, część 1. Jacek Rumiński. Politechnika Gdańska, Inżynieria Biomedyczna

Języki i techniki programowania Ćwiczenia 3 Dziedziczenie

Interfejsy. Programowanie obiektowe. Paweł Rogaliński Instytut Informatyki, Automatyki i Robotyki Politechniki Wrocławskiej

Projektowanie obiektowe oprogramowania Wzorce architektury aplikacji (2) Wykład 10 Inversion of Control Wiktor Zychla 2013

Rozdział 4 KLASY, OBIEKTY, METODY

Podstawy Języka Java

10. Programowanie obiektowe w PHP5

Polimorfizm, metody wirtualne i klasy abstrakcyjne

Builder (budowniczy) Cel: Przykład:

Przykładowa implementacja

Java - tablice, konstruktory, dziedziczenie i hermetyzacja

Interfejsy i klasy wewnętrzne

Kurs programowania. Wykład 2. Wojciech Macyna. 17 marca 2016

Programowanie obiektowe

Podstawy Programowania Programowanie Obiektowe

Prototype (prototyp) Cel: Przykład: Określenie rodzaju tworzonych obiektów poprzez wskazanie ich prototypu. Nowe instancje tworzymy kopiując prototyp.

Technologie i usługi internetowe cz. 2

Wzorce projektowe cz. II. Wzorce projektowe cz. II 1/35

Laboratorium z przedmiotu: Inżynieria Oprogramowania INEK Instrukcja 6

MVVM Light Toolkit. Julita Borkowska

Dziedziczenie. Streszczenie Celem wykładu jest omówienie tematyki dziedziczenia klas. Czas wykładu 45 minut.

Polimorfizm. dr Jarosław Skaruz

Zaawansowane programowanie w języku C++ Programowanie obiektowe

Programowanie obiektowe

Zaawansowane programowanie w C++ (PCP)

Testy jednostkowe Wybrane problemy testowania metod rekurencyjnych

Kurs programowania. Wstęp - wykład 0. Wojciech Macyna. 22 lutego 2016

Wyjątki. Streszczenie Celem wykładu jest omówienie tematyki wyjątków w Javie. Czas wykładu 45 minut.

Projektowanie obiektowe oprogramowania Wzorce architektury aplikacji (3) Wykład 11 Repository, Unit of Work Wiktor Zychla 2016

Java Język programowania

Wykład 9: Polimorfizm i klasy wirtualne

Informatyka I. Klasy i obiekty. Podstawy programowania obiektowego. dr inż. Andrzej Czerepicki. Politechnika Warszawska Wydział Transportu 2018

W przeciwnym wypadku wykonaj instrukcję z bloku drugiego. Ćwiczenie 1 utworzyć program dzielący przez siebie dwie liczby

C++ - [4-7] Polimorfizm

Transkrypt:

Fabryka abstrakcyjna i wszystkie jej odmiany są rodziną konstrukcyjnych wzorców projektowych. Dzięki fabryce otrzymujemy interfejs, służący do generowania różnych obiektów, które go spełniają. Fabryka abstrakcyjna i metoda wytwórcza są bardzo często mylone ze sobą. W wielu źródłach zaprezentowane są ich błędne implementacje, a programiści często sami nie wiedzą, której wersji fabryki chcą użyć. Programowanie bez fabryki W normalnym podejściu do programowania obiektowego, wszelkie instancje nowych obiektów tworzone są za pomocą słowa kluczowego new. Aby utworzyć instancję musimy związać się z jej konkretnym typem, podać go zaraz za rozkazem new. Dodatkowo musimy znać i wypełnić konstruktor. Przy takim podejściu programista nie jest w stanie w łatwy sposób zmienić sposobu tworzenia danego obiektu. Fabryka i wszelkie jej odmiany służą temu, aby odciąć się od podawania konkretnego typu. Zamiast tego, wykorzystana zostaje tzw. metoda fabrykująca zwracająca instancję, która nas interesuje. Krótko mówiąc, zamiast używania rozkazu new i przypisywania instancji do jakiejś zmiennej, wywołujemy metodę fabrykującą np. CreateLoggerInstance() i to ona jest odpowiedzialna, za zwrócenie instancji. Po co używać wzorca fabryki? Dzięki użyciu fabryki programista zyskuje abstrakcyjną warstwę, odpowiedzialną za tworzenie instancji obiektów w jakiś sposób powiązanych wspólnym interfejsem. Uzyskujemy wtedy kod, który jest bardziej skalowalny, łatwiejszy na rozbudowę. Oto najważniejsze zalety używania fabryk: spełnia zasadę odwrócenia zależności (dependency inversion principle). Rodzina tworzonych obiektów spełnia wspólny interfejs, którym następnie się posługujemy. Wpływa to bardzo pozytywnie na testowalność kodu (przykład mechanizmu IoC). Przykładowo zamiast tworzyć nowe klasy new Apple(), new Banana() itd. posiadamy metodę fabrykującą zwracającą obiekty interfejsu IFruit. skupienie logiki w metodzie fabrykującej, dzięki czemu zmiany w kodzie można wprowadzić w jednym miejscu systemu. dostarcza dodatkową warstwę abstrakcji dzięki czemu hermetyzuje (enkapsuluje) odpowiednią logikę wewnątrz fabryki. Hermetyzacja jako filar programowania obiektowego niesie ze sobą szereg kolejnych korzyści m.in. uproszczenie kodu, brak powtarzalności, ukrycie logiki pomiędzy warstwami. upraszcza proces inicjalizacji skomplikowanych klas (w przypadku rozbudowanych konstruktorów). Karol Trybulec p-programowanie.pl 1

w łatwy sposób pozwala na reużywalność kodu (procesu inicjalizacji danej rodziny klas) w innym miejscu systemu spełnia zasadę otwarty na rozbudowę, zamknięty na modyfikację (open/closed principle). Do fabryki w dość łatwy sposób można dodać dodatkową klasę, mając przy tym pewność, że nie zepsujemy czegoś co aktualnie działa. Powyższe zalety fabryk wynikają z bardzo szerokiego spojrzenia na ten wzorzec projektowy. Trzeba pamiętać, że fabryki nie są lekarstwem na całe zło programowania obiektowego. Często są nadużywane w miejscach, gdzie nie powinno ich być. Kiedy użyć wzorca fabryki Zapamiętać należy zasadę: fabryk używamy tam, gdzie chcemy odciąć się od tworzenia instancji klas posługując się konkretnym typem. Może być to spowodowane np. skomplikowaną logiką tworzenia instancji. Np. wiemy, że chcemy uzyskać instancję spełniającą jakiś interfejs, ale jaki to będzie konkretny typ, zależy od dodatkowych parametrów wtedy używamy fabryki. Powody za użyciem fabryk: klasa ma skomplikowany, przeciążony wielokrotnie konstruktor. Oznaczać to może oczywiście błędnie zaprojektowaną klasę, ale może też wskazywać na to, że utworzenie instancji klasy potrzebuje dodatkowej logiki. utworzenie instancji klasy jest poprzedzone instrukcją warunkową if. Z całą pewnością Taką logikę należy zahermetyzować wewnątrz fabryki w momencie pisania programu nie wiesz, której instancji potrzebujesz (bo np. jest to uzależnione od parametru zwracanego z API). Ponownie, logikę hermetyzujemy wewnątrz fabryki. Fabryka abstrakcyjna (i jej odmiany) jest jednym z pierwszych wzorców projektowych, które poznaje programista (obok singletona). Z tego powodu, początkujący programiści często próbują wrzucać fabryki tam, gdzie nie są one potrzebne. Pojedyncza metoda zwracająca instancję jest natomiast bardziej antywzorcem, niż dobrym nawykiem. Idealny przykład do użycia fabryki to kod, który wygląda mniej więcej tak: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 IManageArea managearea; Employee employee = employeeservice.load("managment", 42); if (employee.ismanager) managearea = new GlobalManageArea(); else managearea = new LocalManageArea(); managearea.fireemployees(1000); // itd Karol Trybulec p-programowanie.pl 2

Pomijając sensowność kodu, widać tutaj logikę, od której zależy utworzenie instancji klasy. Całość powinna zostać zahermetyzowana wewnątrz fabryki. Kiedy nie używać wzorca fabryki W następujących sytuacjach użycie wzorca fabryki może okazać się błędne lub co najmniej nadmiarowe: nie tworzyć fabryki na wszelki wypadek, bo może kiedyś będę chciał zainicjalizować obiekt w inny sposób (złamanie zasady YAGNI). nie tworzyć fabryki, jeżeli w jej wnętrzu znajduje się pojedynczy wielki switch bez logiki, zwracający instancję klas z domyślnym konstruktorem. Jest to redundancja i wprowadzenie nowej warstwy abstrakcji (na wszelki wypadek), tam gdzie nie jest ona potrzebna. nie obudowywać kontenerów IoC w fabrykę, bo przeważnie nie jest to konieczne (kontener IoC nie jest fabryką, ale spełnia podobne zadanie i daje podobne efekty) Jak wielu programistów tyle opinii, czy aby na pewno słuszne jest założenie, aby nie tworzyć fabryki na wszelki wypadek, bo być może kiedyś będę potrzebował zainicjalizować obiekt w inny sposób. Pozornym argumentem popierającym tę tezę jest to, że zahermetyzowanie inicjalizacji w osobnej warstwie umożliwi nam w przyszłości zmianę sposobu inicjalizacji w jednym miejscu. Jest to też jedna z zalet wzorca fabryk abstrakcyjnych. Nie należy jednak pchać fabryki wszędzie i próbować rozwiązywać nią problemów, do których nie jest dedykowana. Wykorzystywanie fabryk jako osobnego pojemnika do inicjalizacji obiektów na dłuższą metę okaże się zgubne. Języki obiektowe C#, Java (oraz pół-obiektowe) C++ wymagają związanie klasy z typem za pomocą słowa kluczowego new i nie zbyt wiele programista może z tym zrobić. Użycie prostej fabryki, nie będzie bardziej spełniać zasad SOLIDu, a dobry i testowalny kod da się osiągnąć innymi mechanizmami takimi jak dziedziczenie, kompozycja czy generyczność. Tworzenie prostych fabryk doprowadza w skrajności do sytuacji posiadania super klasy z dziesiątkami metod CreateXXXInstance() co ostatecznie nie ma to nic wspólnego ze wzorcem fabryki. Prosta fabryka (simple factory) Prosta fabryka jest najczęściej używanym rodzajem fabryki. Doskonale sprawdza się w nieskomplikowanych przypadkach. Jest prosta do zaimplementowania a jednocześnie daje programiście korzyści, które wynikają ze stosowania wzorca fabryk. Ta odmiana wzorca, Karol Trybulec p-programowanie.pl 3

moim zdaniem, ma szczególne upodobanie wśród początkujących programistów, którzy chcieliby gdzieś zastosować jakiś wzorzec, a simple factory jest jednym z prostszych do użycia. Oto przykładowy kod: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 enum ShapeType Square = 1, Triangle = 2 interface IShape class Square : IShape class Triangle : IShape class ShapeFactory public IShape CreateShape(ShapeType shapetype) switch (shapetype) case ShapeType.Square: return new Square(); break; case ShapeType.Triangle: return new Triangle(); break; default: throw new ArgumentException(); static void Main(string[] args) ShapeFactory shapefactory = new ShapeFactory(); IShape triangle = shapefactory.createshape(shapetype.triangle); Kod jest trywialnie prosty. Tworzenie poszczególnych instancji zostało zahermetyzowane w osobnej warstwie abstrakcji, którą jest klasa fabryki. W akapicie wyżej napisałem, że nie ma sensu tworzyć fabryk, których jedyną zawartością jest wielki, nic nie wnoszący switch, jednak jest to tylko przypadek testowy. Karol Trybulec p-programowanie.pl 4

Wadą prostej fabryki jest to, że nie spełnia drugiej zasady SOLID czyli Open/closed principle. Mimo wielu zalet jakie uzyskał programista decydując się na tę fabrykę, rozbudowa jej o nowe elementy niesienie ze sobą konieczność ingerencji w klasę fabryki. Konkretniej oprócz rozszerzenia interfejsu IShape o nowe klasy, należy rozbudować instrukcję switch wewnątrz klasy fabryki. Można też zastosować wersję statyczną. Metoda zwracająca instancje może być oznaczona jako static wtedy nie trzeba będzie za każdym razem tworzyć instancji fabryki. Wybór oczywiście zależy od specyfikacji systemu, jednak ja stanowczo trzymam się od klas statycznych z daleka. Klas statycznych nie da mockować, wcześniej czy później są z nimi problemy (szczególnie platforma ASP.NET MVC). Użycie klas statycznych często oznacza brak możliwości wprowadzenia testów jednostkowych. Metoda fabrykująca (factory method) Metoda fabrykująca to najczęściej używany rodzaj fabryki, wśród programistów, którzy dobrze rozumieją działanie poszczególnych rodzajów fabryk. Jest to odmiana najbardziej uniwersalna. Oprócz niesienia ze sobą wszystkich korzyści wynikających ze wzorca fabryki, spełnia też pierwszą zasadę SOLID czyli open/closed principle. Dlaczego? Ponieważ kod odpowiedzialny za tworzenie instancji został przeniesiony do klas potomnych poszczególnych fabryk. Oto przykładowy kod: Karol Trybulec p-programowanie.pl 5

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 interface IShape class Square : IShape class Triangle : IShape abstract class ShapeFactory public abstract IShape CreateShape(); class SquareFactory : ShapeFactory public override IShape CreateShape() return new Square(); class TriangleFactory : ShapeFactory public override IShape CreateShape() return new Triangle(); static void Main(string[] args) ShapeFactory shapefactory = new SquareFactory(); IShape square = shapefactory.createshape(); Jak widać w powyższym kodzie klasa fabryki jest teraz klasą abstrakcyjną. Logika umieszczona w switchu prostej fabryki została zastąpiona mechanizmem polimorfizmu. Proces tworzenia instancji klas został wydelegowany do klas potomnych, i to one decydują, jaką instancję zwrócić. Dzięki temu, przy rozbudowie powyższego kodu programista nie musi zmieniać klasy fabryki, co oznacza że spełniona jest zasada open/closed principle. Dopisanie nowego typu polega jedynie na dopisaniu nowej fabryki. Powyższy przykład jest prosty, aby maksymalnie ukazać koncepcje metody fabrykującej. Dzięki temu, że korzystamy z metody fabrykującej mamy jednak kolejną ważną zaletę, której nie widać w przykładzie. Klasa abstrakcyjna może posiadać metody, które odziedziczą klasy potomne. Dzięki temu mamy większe pole możliwości niż w prostej fabryce, której sednem działania była instrukcja warunkowa switch. Karol Trybulec p-programowanie.pl 6

Zalety metody fabrykującej spełnia zasadę SOLID open/closed principle, jest łatwiejsza do późniejszych modyfikacji. główny kod fabryki może być dystrybuowany w postaci osobnej, zamkniętej biblioteki DLL, mimo to programista i tak będzie miał możliwość jej rozbudowy. dodatkowa warstwa abstrakcji, która umożliwia hermetyzację skomplikowanej logiki tworzenia instancji obiektu w klasach pochodnych (w prostej fabryce wszystko byłoby w switchu). poszczególne fabryki pochodne można w łatwy sposób testować jednostkowo, należy tylko wprowadzić dodatkowy interfejs, które będą spełniać (oprócz klasy abstrakcyjnej). Prostej fabryki natomiast testować się nie da. Fabryka abstrakcyjna (abstract factory) Fabryka abstrakcyjna jako ostatnia z odmian wzorca fabryk sprawia zawsze najwięcej kłopotów. Główna zasada mówi, że wzorzec fabryki abstrakcyjnej ma dostarczyć interfejs do tworzenia rodziny obiektów, konkretniej oddzielić interfejs od implementacji. Najważniejszym słowem kluczowym jest tutaj rodzina obiektów, która nie występuje w przypadku dwóch poprzednich fabryk. Ale, że o co chodzi? Dwóch poprzednich fabryk, czyli fabryki prostej i metody fabrykującej używamy, gdy chcemy tworzyć obiekty spełniające jeden interfejs. W poprzednich akapitach tworzyliśmy figury geometryczne spełniające interfejs IShape. Nie możemy przedstawić poprzedniego przykładu w formie fabryki abstrakcyjnej, ponieważ figury geometryczne to pojedynczy typ, a nie rodzina obiektów. Przykład z figurami geometrycznymi nie jest zbyt trafny, ponieważ ciężko na szybko wymyślić jakąś sensowną rodzinę obiektów. Załóżmy jednak, że chcemy tworzyć typy figur spełniające interfejs IShape oraz zbiory liczbowe spełniające interfejs INumber. Obydwa typy tworzą rodzinę obiektów MathTest, czyli naszą fabryką abstrakcyjną będzie fabryka zwracająca test. W momencie gdy zdefiniowaliśmy umowną rodzinę obiektów, możemy zamodelować fabrykę abstrakcyjną. MathTest będzie naszą fabryką abstrakcyjną, a konkretne fabryki abstrakcyjne będą jej różnymi odmianami. Spójrzmy na przykładowy kod: Karol Trybulec p-programowanie.pl 7

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 // kształty interface IShape class Square : IShape class Triangle : IShape // liczby interface INumber class RealNumber : INumber class ComplexNumber : INumber // fabryka abstrakcyjna rodziny obiektów abstract class MathTestFactory public abstract IShape CreateShape(); public abstract INumber CreateNumber(); // konkretna fabryka rodziny obiektów class PrimarySchoolTestFactory : MathTestFactory public override IShape CreateShape() return new Square(); public override INumber CreateNumber() return new RealNumber(); // konkretna fabryka rodziny obiektów class HighSchoolTestFactory : MathTestFactory public override IShape CreateShape() return new Triangle(); public override INumber CreateNumber() return new ComplexNumber(); // klasa klienta (kontekst wykonania fabryki) class MathTest private MathTestFactory mathtestfactory; public MathTest(MathTestFactory mathtestfactory) this.mathtestfactory = mathtestfactory; public void GenerateTest() var shape = this.mathtestfactory.createshape(); var number = this.mathtestfactory.createnumber(); System.Console.WriteLine("Test wygenerowany"); static void Main(string[] args) MathTest mathtest; mathtest = new MathTest(new PrimarySchoolTestFactory()); mathtest.generatetest(); // łatwy test przy użyciu PrimarySchoolTestFactory() mathtest = new MathTest(new HighSchoolTestFactory()); mathtest.generatetest(); // trudny test przy użyciu PrimarySchoolTestFactory() Karol Trybulec p-programowanie.pl 8

Klasa MathTestFactory jest fabryką abstrakcyjną definiującą rodzinę obiektów. Za pomocą kompozycji definiujemy, że obydwa elementy są w jakiś sposób od siebie zależne. Na potrzeby artykułu przyjęliśmy, że tworzą test. Następnie tworząc konkretne egzemplarze fabryk, możemy rodzinę obiektów stworzyć na wiele sposób i tak np. PrimarySchoolTestFactory tworzy rodzinę obiektów bardzo prostych (kwadrat i zbiór liczb rzeczywistych). Natomiast HighSchoolTestFactory tworzy rodzinę obiektów trudnych (trójkąt i zbiór liczb zespolonych). Ostatnim najważniejszym elementem fabryki abstrakcyjnej jest klasa klienta (kontekstu wykonania fabryki). Za pomocą kompozycji tworzymy klasę zawierającą odpowiednią fabrykę. Często w klasie klienta istnieje też namiastka wzorca projektowego metody szablonowej, która wywołuje metody fabryki (w kodzie wyżej metoda GenerateTest()). Klasa klienta służy do obsługi logiki wymaganej przy tworzeniu rodziny obiektów, hermetyzuje inicjalizację rodziny obiektów oraz ich konfigurację. Klient nie wie, jaka fabryka zostanie mu dostarczona i wywołana ważne aby spełniała typ fabryki abstrakcyjnej. Rola poszczególnych elementów fabryki abstrakcyjnej fabryka abstrakcyjna definiuje umowną rodzinę obiektów. konkretna fabryka abstrakcyjna implementuje fabrykę abstrakcyjną, dostarcza implementację jej metodom, zawiera logikę. klient musi zawierać odniesienie do fabryki abstrakcyjnej (za pomocą kompozycji), zawiera metodę (na wzór metody szablonowej) wywołującą metody dostarczonej fabryki, nie wie z jakiej fabryki będzie korzystał (logika zahermetyzowana w innych warstwach). Mimo zalet wzorca fabryki abstrakcyjnej, niesie on ze sobą także wady. Jest o wiele trudniejszy do rozbudowy, łamie zasadę open/closed principle. Dodając nowy produkt, trzeba zmodyfikować wiele innych klas np. konkretne fabryki, a dodając nowy typ do rodziny obiektów trzeba zmodyfikować klasę fabryki abstrakcyjnej. Czasem trzeba zmodyfikować także kod klasy klienta. Podsumowanie wzorca fabryki Wszelkie odmiany fabryki abstrakcyjnej potrafią przysporzyć wiele problemów nawet zaawansowanym programistom. Osobiście wzorzec fabryki uważam za najbardziej skomplikowany wzorzec gangu czworga (gank of fours ojcowie wzorców i paradygmatu OOP). Jego niezwykła komplikacja polega na wielu możliwościach implementacji, a niniejszy artykuł pokazał tylko małą jej część. Przykłady zawarte w artykule są ubogie, a poszczególne rodzaje fabryk można znacząco modyfikować i łączyć z innymi wzorcami np. wzorcem Karol Trybulec p-programowanie.pl 9

strategii i wzorcem polecenia. Mimo to, poniższe wskazówki pomogą Ci ogarnąć temat fabryk: Prosta fabryka najprostsza możliwość oddzielenia implementacji od interfejsu. brak dziedziczenia i brak kompozycji. brak osobnej klasy klienta. Prosta fabryka zawsze zwraca obiekt spełniający jeden interfejs. Jest ciężka do testowania i ciężka do rozbudowy. Czasem (niesłusznie) uważana za antywzorzec, bo bywa nadużywana tam gdzie wcale nie powinno być fabryki. Metoda fabrykująca najbardziej uniwersalna możliwość oddzielenia implementacji od interfejsu (najlepiej spełnia open/closed principle). zawsze występuje mechanizm dziedziczenia. brak kompozycji. brak osobnej klasy klienta. Metoda fabrykująca zawsze zwraca obiekt spełniający jeden interfejs. Jest bardzo skalowalna, idealna do testowania i rozbudowy. Zawsze korzysta z mechanizmu dziedziczenia i polimorfizmu, przenosząc odpowiedzialność na klasy pochodne (czyli konkretne fabryki). Fabryka abstrakcyjna umożliwia tworzenie rodzin obiektów w jakiś sposób powiązanych ze sobą. zawsze występuje mechanizm dziedziczenia. zawsze występuje mechanizm kompozycji. zawsze występuje dedykowana klasa klienta. Minusem fabryki abstrakcyjnej jest mała skalowalność, dodając nowy produkt trzeba przerobić klasę fabryki abstrakcyjnej oraz konkretnych fabryk. Często nazywana jest fabryką fabryk, ponieważ tak naprawdę korzystając z mechanizmu kompozycji zwraca metody fabrykujące. Zawsze występuje mechanizm dziedziczenia i polimorfizm (do konkretnych fabryk) oraz mechanizm kompozycji (do powiązania klienta z określoną fabryką). Jej sednem jest to, że klient wykonuje jakieś operacje, ale nie wie z jakiej fabryk korzysta. Bez osobnej klasy klienta jest po prostu metodą fabrykującą. Bez zwracania rodziny obiektów (tylko pojedynczego typu) jest po prostu metodą fabrykującą. Mam nadzieję, że ten artykuł choć trochę rozjaśnił Ci rodzaje fabryk oraz różnic, jakie między nimi występują. Karol Trybulec p-programowanie.pl 10