AngularJS #8 – Factory vs Service vs Provider

AngularJS



AngularJS daje nam możliwość pisania kodu na wiele różnych sposobów. Jedną z tendencji jaką zauważam na StackOverflow jest to, że ludzie starają się upchać do swoich kontrolerów wszystko co tylko możliwe – łącznie z logiką biznesową, pobieraniem / zapisywaniem danych, operacjach na DOM, czy przetwarzaniu wielkich struktur danych. Jest to wprawdzie domena tych mniej doświadczonych, jednak warto by każdy starał się odchudzać swoje kontrolery już na etapie pisania pierwszego kodu, a nie na etapie refaktoringu lub później (albo w ogóle!).

Kontrolery

Kontrolery w AngularJS mają to do siebie, że tworzone są (umieszczane w pamięci przeglądarki) w ramach tego jak są potrzebne i usuwane, gdy przestają być używane. Wyobraźmy sobie przechodzenie pomiędzy różnymi stronami naszej aplikacji, pomiędzy różnymi definicjami routingu, gdzie dla każdej strony wywoływany jest osobny kontroler. Następnie wyobraźmy sobie, że wszystkie elementy w kontrolerach tworzone i usuwane są za każdym takim przejściem ze strony do strony. Jest to niebywałe marnotrastwo zasobów, co wpływa także na wydajność pisanej przez nas aplikacji. Całą tą logikę odpowiedzialną chociażby za pobieranie danych lub ich zapisywanie po stronie API (backendu) jesteśmy w stanie przenieść do osobnych serwisów, które to wstrzyknięte do naszego kontrolera pozwolą zrealizować dokładnie taką samą funkcjonalność. Serwisy mają tą przewagę, że tworzone są jednorazowo (jako singletony) i wstrzykiwane mogą być jako zależność do wielu kontrolerów.

Factory vs Service vs Provider

Problem może pojawić się na etapie wyboru odpowiedniej drogi, która pozwoli nam zrealizować daną funkcjonalność. AngularJS daje nam obecnie trzy sposoby na odchudzenie naszych kontrolerów poprzez użycie:

  • Factory
  • Service
  • Provider

Poniżej postaram się omówić każdy z nich oraz wskazać różnice, które mogą zadecydować o konkretnym wyborze. Przykłady będą bajecznie proste, więc z pewnością każdy poradzi sobie z ich przyswojeniem. Celowo nie wykorzystuję tutaj $http, czy $resource, by nie komplikować i zaciemniać kodu. Znając oba mechanizmy możecie posłużyć się nimi do zbudowania pełnoprawnego kodu serwisu, który obsługiwać będzie logikę biznesową Waszej aplikacji. Przechodząc jednak do konkretów..

1 ) Factory

Factory to w zasadzie najprostsza wersja serwisów w AngularJS. Tworząc factory, tworzymy de facto obiekt z polami w formie zwykłych zmiennych lub funkcji, który następnie zwracamy poprzez użycie return. Wstrzyknięte do kontrolera factory jest dokładnie takim prostym obiektem. Posłużę się przykładem, który lepiej zilustruje to co mam na myśli.

Spójrzcie od razu na zakładkę JavaScript. Stworzyłem tam naiwny kontroler, którego jedynym zadaniem jest ustawienie nazwy użytkownika poprzez wywołanie metody setUsername() serwisu User wstrzykniętego jako dependency do kontrolera oraz wywołanie metody sayHello() tegoż serwisu w celu pobrania wiadomości powitalnej dla ustawionej nazwy użytkownika.

Poza kontrolerem zdefiniowany został serwis User typu factory. W ciele tego serwisu mamy trzy zmienne lokalne (_username, _setUsername, _sayHello), które widoczne są tylko w ramach tego serwisu. Następnie zwracany jest nowy obiekt za pomocą polecenia return z dwoma polami (setUsername, sayHello), do których przypisane zostają referencje zmiennych lokalnych (funkcji), odpowiednio _setUsername oraz _sayHello. Równie dobrze jednak, nasz serwis factory mógłby wyglądać tak:

Ograniczamy się w tym momencie do jednej zmiennej lokalnej w kontrolerze User.

Poprzez wstrzyknięcie factory User do kontrolera, otrzymujemy w nim dostęp do pól zwróconego obiektu. Działanie jest tak proste, że nie zamierzam tego komplikować kolejnymi wyjaśnieniami.

2) Service

Kolejny serwis nazwany został dosłownie Service. W porównaniu do FactoryService tworzony jest na zasadzie użycia słowa new, a następnie zostaje wstrzyknięty do kontrolera. Użycie konstruktora (new) powoduje, że wszystkie widoczne w serwisie zmienne przypisane muszą zostać do słowa this, które widoczne jest po wstrzyknięciu serwisu do kontrolera. Spójrzcie na ten sam przykład, ale z wykorzystaniem Service.

Widać wyraźnie, że kod po stronie kontrolera pozostał taki sam. Serwis natomiast wygląda nieco inaczej. Nie ma już słowa return i zwracanego w ten sposób obiektu z metodami setUsername oraz sayHello. Zamiast tego, metody te przypisane zostały do słowa kluczowego this, które zwrócone zostanie do kontrolera w ramach wywołania konstruktora przy użyciu new przez $injector.

3) Provider

Provider jest jedynym serwisem, który użyty może być w ramach config naszej aplikacji. Dzięki temu jesteśmy w stanie dokonać ustawień naszego serwisu jeszcze zanim zostanie on wstrzyknięty i użyty w kontrolerze. Struktura provider jest nieco rozdmuchana, ale w gruncie rzeczy przypomina on factory oraz service. Poniżej ciągle ten sam przykład, ale z wykorzystaniem provider.

Zmian jest sporo. Na samym końcu zobaczyć możecie nową sekcję: config. Służy ona do ustawiania parametrów naszej aplikacji, a w tym wypadku do ustawienia pola naszego serwisu. W samym provider mamy teraz zmienną _username przypisaną do słowa this i tylko takie zmienne widoczne są z poziomu config aplikacji. Następnie do this przypisana zostaje funkcja $get, która z kolei zwraca nam prosty obiekt za pomocą słowa return do kontrolera. Tylko obiekt zwrócony przez funkcję $get naszego providera widoczny jest po tej drugiej stronie (kontrolerze). W kontrolerze ubyło ustawianie nazwy użytkownika, która przeniesiona została do części config. Warto zauważyć, że do ciała funkcji config wstrzykiwany jest UserProvider, a nie User. Zapamiętajcie, że w ten sposób możemy dobrać się do pól przypisanych do this naszego providera. Gdybyśmy nasz provider nazywał się np. myProvider, to do config musielibyśmy wstrzyknąć myProviderProvider. Za każdym razem jest to dodanie słowa Provider do oryginalnej nazwy. Pozostaje również zapamiętać, że tylko serwis provider pozwala nam na ustawienie zmiennych w sekcji config aplikacji.

Krótko podsumowując

Tym oto prostym sposobem udało mi się (mam nadzieję) pokazać zasadnicze różnice pomiędzy różnymi typami serwisów w AngularJS. Zapraszam tym samym do podzielenia się swoimi wnioskami w komentarzach oraz do odchudzania swoich kontrolerów.

  • Grupal

    Świetne wpisy o Angular! Dobra robota!

  • Bardzo fajna seria tylko service z ang. oznacza usługę. To chyba lepiej oddaje znaczenie fabryki, dostawcy itd.

    • Pewnie tak. Dzięki za uwagę. ANG jest w naszej branży tak powszechny, że często się nie zastanawiamy nawet nad tym, że mieszamy go z PL.

      • Kuba

        Osobiście podoba mi się, gdy w polskich artykułach kluczowe nazwy nie są spolszczane. Co do treści artykułu, to przydałoby się podać przykłady kiedy używać, a kiedy nie używać factory/service/providera.

      • W zasadzie w treści tego artykułu jest o tym mowa. Fakt, że są to podstawy, ale główny przekaz został zachowany. Czego Ci brakuje? :)

      • ipod69

        Ja też chętnie bym poznał taką “matrycę” wad i przewag pomiędzy tymi trzeba “bytami”. Swoj pierwszy serwis napisałem w oparciu o .factory, gdyż widziałem, że w ten sposób łatwo panuję nad tym co w kodzie pozostaje prywatne a co udostępniam publicznie. Widzę, że Provider też to umożliwia. W jakich przykladowych sytuacjach użycie .service byłoby nieodzowne?

      • Użycie .service od .factory w zasadzie różni się tym, że w przypadku .service posługujemy się standardowym konstruktorem JS. Dla mnie to mniej czytelne podejście, w stosunku do .factory i w zasadzie korzystam jedynie z .factory i .provider, który pozwala wykonać coś w stylu @Before.

      • Son

        Macieju mówisz że opisałeś różnice pomiędzy każdym z powyższych zagadnień, ale na moje to skupiłeś się głównie nad składnią i tworzeniem tych bytów. Z artykułu za bardzo nie wynika czym się różnią pomiędzy sobą w kontekście cyklu życia, dostępności, wydajności czy czegokolwiek innego.

      • To prawda. Nie ma o tym za dużo. Artykuł powstał już jakiś czas temu – zaczynałem też wtedy swoją przygodę z AJS. Dziś napisałbym więcej. Plan na całą serię też obejmował znacznie więcej zagadnień. Jak zwykle zatrzymałem się na “niedoczasie”. Postaram się w tym roku ugryźć temat AngularJS 2.x, ale to też jak czas pozwoli. Dzięki za Twój komentarz. Poprawię się na przyszłość ;)

  • Adam Mocarski

    Bardzo fajny tutorial. Pozwala szybko zapoznać się z ogólnym zarysem tej technologii i nie trzeba przedzierać się przez tony kodu i opisów jak w rozbudowanych, szczegółowych tutorialach.

  • Gronekmaster

    Fajny tutorial, ale czy będzie kontynuowany?

    • Skupię się już raczej na Angular 2.x, ale to jeszcze trochę. Twórcy mają jeszcze nieco przed sobą.

  • junik91

    witam !
    Macieju jaka jest więc poprawna/ dobra praktyka w pobieraniu danych z serwera (przyjmijmy że opartego na node.js) który z tych 3 przedstawionych wyżej sposobów zapewni optymalne działanie aplikacji ?

    pozdrawiam

    • Wszystko zależy w sumie od potrzeb. Sam nie stosuję .service, bo .factory wydaje mi się dużo czytelniejszym rozwiązaniem. Jeżeli jednak potrzebuję sobie coś skonfigurować, to z pomocą przychodzi .provider.

      Masz jakiś konkretny use case w swoim przypadku? :)

      • junik91

        Projekt restauracji gdzie pobieram sobie dania, składniki, dodatki i User ma możliwość modyfikowania i tworzenia dań :D (Projekt semestralny na uczelni)

  • Tomasz Jarmuż

    Super artykuł! Dzięki za rozwianie wątpliwości!

  • Pingback: Wymiana danych pomiędzy kontrolerami czyli serwisy | Marcin Kowalczyk – Blog IT()

  • Pingback: Wymiana danych pomiędzy kontrolerami w AngularJS czyli serwisy | Marcin Kowalczyk – Blog IT()

  • Bulwasnk

    Tyle co czytałem dokumentacje Angularową o serwisach i nie do końca jeszcze widziałem różnice pomiędzy nimi. Potem trafiłem na ten wpis i wszystko stało się ostatecznie jasne. Polecam każdemu tak zrobić. Świetny wpis!