Przyszła pora, aby zapoznać się z kolejnym wzrocem projektowym, a tym razem padło na Factory, który należy do rodziny Creational Patterns. Jest to wzorzec stosunkowo prosty w implementacji i przydatny w sytuacji, gdy mamy kilka obiektów (klas, struktur), które korzystają z wspólnego protokołu, czy klasy bazowej. Jeżeli komponenty nie mają wspólnego mianownika wzorzec Factory nie sprawdzi się.

Jak zawsze trochę teorii się przyda, ale moja rada jest taka żebyście z grubsza przeczytali o co chodzi i wrócili tutaj jak już zobaczycie jak wygląda implementacja wzorca w praktyce. Wzorzec Factory (Fabryka) pozwala ukryć przed komponentem wywołującym szczegóły związane z tworzeniem danego obiektu. Kod zwarty w naszym wzorcu będzie decydował jaki konkretnie obiekt ma zostać utworzony, a komponent, który go potrzebuje będzie sobie żył w błogiej niewiedzy. On tylko mówi, czego mu potrzeba, a to Fabryka jest odpowiedzialna z dostarczenie produktu zgodnego z zamówieniem.

Dużą zaletą takiego rozwiązania jest fakt, że będziemy mogli oprzeć implementację na abstrakcji (w ramach komponentu wywołującego) zamiast na konkretnym typie. To z kolei pozwoli nam na zmiejszenie zależności pomiędzy komponentami, ułatwi pisanie testów oraz zastosowanie zasady Dependency Inversion Principle (DIP). Wzorzec zostanie zaimplementowany prawidłowo, jeżeli wywołujący komponent dostanie to czego potrzebował i jednocześnie nie dowie się w jaki sposób zostało to osiągnięte.

Fabryka ukrywa bardzo głęboko sposób w jaki tworzone są poszczególne obiekty (oraz zależności pomiędzy nimi), a komponent, który będzie chciał skorzystać z jej rozwiązania, informuje tylko jaki protokół lub klasa bazowa zostanie wykorzystana. Operacja ta składa się z trzech kroków:

 

  1. Wywołujący komponent podaje do metody (będącej częścią fabryki) argumenty, na podstawie których ma zostać podjęta decyzja jaki element (obiekt) ma zostać w fabryce utworzony.
  2. Logika (kod) umieszczony w Fabryce decyduje na podstawie argumentów, jaki obiekt zostanie zwrócony do komponentu wywołującego.
  3. Fabryka tworzy wybrany obiekt i zwraca go do zamawiającego (komponentu wywołującego).

 

Jak widzicie takie rozwiązanie pozwala ukryć wszystko przed zamawiającym. Nie jest on nawet w stanie powiedzieć ile i jakie obiekty Fabryka jest w stanie wytworzyć. Wie tylko, że zwrócony obiekt będzie spełniał wymogi określonego protokołu lub klasy bazowej.

Kiedy przyda nam się to w życiu codziennym? Ja wykorzystałem to w aplikacji, w której użytkownik mógł zalogować się na kilka różnych sposobów – email, Twitter, Google, Facebook. Przed każdą zmianą swoich danych, użytkownik musiał zostać ponownie zweryfikowany. Wzorzec Fabryka posłużył mi do wybrania metody właściwej dla danego użytkownika. Poniżej umieściłem jego implementację.

W pierwszej kolejności utworzyłem model dla użytkownika aplikacji. Podaję tutaj uproszczoną wersji, aby przykład był bardziej przejrzysty:

 

 

W tym przypadku Swift automatycznie utworzy dla nas konstruktor zawierający wszystkie pola (działa to tylko ze strukturami), dlatego nie ma konieczności tworzenia osobno konstruktora.

Następnie utworzyłem protokół, na którym będą opierały się obiekty tworzone przez fabrykę. Tutaj również dla uproszczenia zaimplementowałem tylko jedną funkcję:

 

 

Teraz przyszedł czas na utworzenie obiektów, które będą spełniały wymogi protokołu ReAuthUserProtocol i będą wykorzystywane przez obiekt Fabryki:

 

 

A tak prezentuje się sam obiekt Fabryki:

 

 

I sam koniec prosty przykład wykorzystania:

 

 

Jeżeli całość nie od razu zaskoczy u Was w głowie to się nie przejmujcie. Rozpiszcie sobie wszystko najlepiej sami, a bardzo szybko się we wszystkim połapiecie. Ze wzorcami już tak jest, że trzeba każdy z nich kilka razy zaimplementować, żeby dokładnie go zrozumieć. No chyba, że mówimy o Singletone 😝.

Nie twierdzę oczywiście, że jest to najlepsza i jedynie słuszna implementacja, ale w moim przypadku sprawdziła się ona bardzo dobrze.


 

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *