P-programowanie https://www.p-programowanie.pl Blog poświęcony programowaniu we wszystkich popularnych językach i maturze z informatyki. Thu, 08 Feb 2018 21:16:22 +0000 pl-PL hourly 1 https://wordpress.org/?v=4.9.4 Studia nie uczą programowania https://www.p-programowanie.pl/studia-praca/studia-nie-ucza-programowania/ https://www.p-programowanie.pl/studia-praca/studia-nie-ucza-programowania/#comments Thu, 28 Sep 2017 21:45:15 +0000 https://www.p-programowanie.pl/?p=3450 Istnieje wiele sposobów na osiągnięcie sukcesu w IT. Jednym z najpopularniejszych sposób wejścia do branży jest ukończenie studiów informatycznych. Jest to bardzo dobry start na rynku, jednak wielkie zastrzeżenia można mieć do samej metodyki. Czego można spodziewać się po ukończeniu studiów? W artykule skupię się przede wszystkim na nauce programowania.

Studia nie nauczą Cię programowania

Programowanie, tak jak cała branża IT, rozwija się tak szybko, że nikt nie jest w stanie umieć wszystkiego. Co kilka miesięcy lub lat powstają nowe wersje języków programowania oraz nowe platformy. Niektóre są podobne do poprzednich, inne całkowicie przebudowane. Zmienia się także sposób podejścia do planowania poszczególnych rozwiązań i architektur systemów informatycznych.

Aby nie trzymać Cię w niepewności, napiszę na samym początku: studia informatyczne nijak nie nauczą Cię programować. Piszę to z pełną świadomością jako zawodowy programista i absolwent studiów informatycznych. Nieważne, jak renomowaną uczelnię wybierzesz, czy będzie to uczelnia publiczna czy prywatna, czy studia dzienne czy zaoczne. Jakie są tego przyczyny?

Hermetyczne środowisko profesorów

Problem z uczelniami wyższymi leży w hermetycznym środowisku akademickim. Wykładowcy akademiccy nie zdobywają doświadczenia zawodowego przy komercyjnych projektach, nie spędzają nad programowaniem 8h dziennie. Oprócz tego ogranicza ich rama programowa i struktura narzucona przez Ministerstwo Nauki i Szkolnictwa Wyższego. Wszelkie technologie nauczane na studiach są totalnie przeterminowane i bezużyteczne.

Dosłownie nikomu nie radziłbym w dzisiejszych czasach zaczęcia nauki programowania od języka C++. O ile był on dawniej sławny, o tyle teraz jest bezużyteczny. Ciągle chętnie używają go programiści niskopoziomowi, embedded i elektronicy. Nie nadaje się on natomiast do nauki i projektów komercyjnych. Istnieje wiele nowszych języków wysokiego poziomu, które pozwolą nauczyć się programować szybciej i efektywniej.

C++ jest natomiast stałym bywalcem każdej uczelni wyższej. Czarna konsola napawa niechęcią, wielu mechanizmów w C++ brakuje, a z kolei z wielu się już nie korzysta (bo są lepsze rozwiązania).

Brak możliwości zatrudniania programistów

Przez deficyt na rynku pracy programistów ciągle brakuje. Stąd biorą się dzisiaj ich wysokie zarobki. Żadna uczelnia wyższa nie jest w stanie zatrudnić doświadczonego programisty do prowadzenia wykładów lub ćwiczeń. Co ciekawe, jest to spowodowane względami finansowymi, ale także – jak wspomniałem wcześniej – hermetycznym środowiskiem akademickim. Każdy dba o swoją posadę i nikomu nie byłoby na rękę, gdyby jeden z przedmiotów wziął ktoś „spoza” uczelni.

Jaki jest tego efekt? Na wykładach z C++ wykładowca nie umie wytłumaczyć, do czego służą wskaźniki. Nie umie znaleźć sytuacji praktycznej, w jakiej dałoby się je wykorzystać. Później nie dość, że studenci uczą się archaicznego języka, to słuchając o wskaźnikach czują się jak w kosmosie. Oczywiście, podkreślam, nie chodzi o nieumiejętność wytłumaczenia wskaźników. Wykładowcy akademiccy przeważnie bardzo dobrze umieją teorię, ale w przekazywaniu wiedzy liczy się też otoczka.

Bardzo sucha teoria

Na studiach poznaje się bardzo wiele teoretycznej wiedzy, bez jakichkolwiek przykładów jej wykorzystania. Nauka wzorców projektowych, algorytmów, metodyk wytwarzania programowania przebiega płynnie, ale student nawet nie wie, po co się tego uczy. Oczywiście ta wiedza kiedyś się przyda, jednak w momencie studiowania jest jej za dużo i jest zbyt niepoukładana.

Dobry student po dobrej uczelni, wkraczając na rynek pracy programistów, jest początkowo bezużyteczny. Potrzebuje dużo czasu i opieki doświadczonego programisty, aby móc coś sensownego tworzyć.

Praktyka? Projekty na studiach

Jeżeli zastanawiasz się, dlaczego na studiach informatycznych nie ma więcej praktyki, od razu odpowiem – jest. Istnieją duże przedmioty nazywane „projektami semestralnymi”, które pochłaniają w sylabusach ogromne ilości godzin i są poświęcone właśnie praktycznemu wykonywaniu jakichś programów.

Problem z projektami jest natomiast taki, jak w pierwszych akapitach tego artykułu:

  • prowadzą je wykładowcy z wiedzą sprzed 5-10lat
  • prowadzą je wykładowcy bez doświadczenia zawodowego
  • studenci nie mogą z nikim kompetentnym skonsultować architektury i fundamentów projektu
  • prowadzący je wykładowca kompletnie nie ma czasu dla studentów, grupy są za duże, a czasu za mało

Efektem tych zajęć są totalnie „słabo wykonane systemy”. Bez żadnych wzorców projektowych, mechanizmów, rozwiązań architektonicznych. Pełne instrukcji warunkowych, które są później bohaterami zabawnych memów w branży IT. Wszystkie projekty są pisane w PHP. Oczywiście bez żadnej dodatkowej platformy, bez wspomnienia o wzorcu MVC. Po prostu zwykłe instrukcje PHP powplatane w kod HTML, często przechowujące informacje w plikach tekstowych.

Przykład z moich studiów, którego doświadczyłem. Profesor od programowania polecił napisać w C++ grę w kółko krzyżyk. Był to projekt semestralny, od prowadzącego nie dostaliśmy żadnej instrukcji ani nakierowania. Efekt? Nikt nie zastosował algorytmu minimax, każdy oddał projekt z 400-stoma instrukcjami warunkowymi obsługującymi każdą możliwą kombinację kółka i krzyżyka na planszy.

Takie projekty nie dość, że nic nie dają, to utrwalają bardzo złe nawyki.

Niechlubna para C++ i PHP

Para języków C++ i PHP to ta podstawowa, poznawana na studiach informatycznych. Jeżeli przeglądnąć fora internetowe, to właśnie w tych językach jest powrzucanych setki „programów studenckich” będących plątaniną instrukcji warunkowych. Autorzy postów przeważnie proszą o dopisanie czegoś, bo brakuje do zaliczenia. W innym wypadku pytają czemu program nie działa; w kodzie brakuje wcięć, czasem średników czasem jakieś klamerki.

Wielu studentów, kończąc uczelnie wyższą, wie że:

  • programy dekstopowe to te pisane w C++
  • strony internetowe to te pisane w PHP i HTML

Na tym wiedza się kończy. Na studiach nikt nie porusza nic innego. O JavaScriptcie panuje przekonanie takie jak w książkach z 2003 roku, czyli że nie powinno się tego używać. Strona z JavaScriptem to nie jest prawdziwa strona. JavaScript służy tylko do tworzenia kalendarzy i zegarków.

Tymczasem mało kto wie, że obecnie JavaScript jest najczęściej używanym językiem na świecie, a od 2008 roku przeżywa bardzo silny rozwój. Mało kto wie, że stronę można zrobić w czymś innym niż PHP, że aplikacji internetowych nie robi się już w modelu request-response tylko na reużywalnych asynchronicznych komponentach. Mało kto wie, że w wielu dziedzinach odchodzi się od aplikacji desktopowych i wprowadza się aplikacje internetowe i chmury.

Dlaczego o tym piszę w dość agresywnym tonie? Po prostu, ludzie kończąc studia nagle dowiadują się, że istnieje 20 języków programowania, a do każdego około 5-20 platform programistycznych. Z czego na studiach poznali te, które nie są już używane.

Czy warto studiować?

Artykuł niesie proste przesłanie: studia jeszcze z nikogo nie zrobiły programisty. Jeżeli chcesz być programistą, warto skończyć studia informatyczne, ale pamiętaj, dzięki nim nie staniesz się programistą. Być może ukierunkujesz się na wiele aspektów, o których nie miałeś pojęcia. Ale nic poza tym.

Studia warto skończyć. Jak wspomniałem wcześniej, wiedza teoretyczna ze studiów przyda się w dalszej ścieżce zawodowej. Więcej na ten temat opisałem w artykule dlaczego warto studiować informatykę? Jeżeli masz możliwości (przede wszystkim te finansowe), bez wątpienia skończ studia. Jeżeli chcesz nauczyć się programować, przeczytaj następny akapit.

Jak nauczyć się programowania

Do nauki programowania potrzeba tylko jednego: chęci i wielu godzin ćwiczeń. Doskonałymi źródłami wiedzy są książki oraz kursy internetowe. Wybierz jakiś język, który jest aktualnie na topie i po prostu zacznij. Rankingi języków (czyli popularność i opłacalność) na stan dzisiejszy dostępne są w internecie.

Mylisz się jeżeli sądzisz, że to co napisałem to lanie wody albo pusty akapit. Programowanie zmienia się tak szybko, że jeżeli nie umiesz uczyć się sam, to nie jest to zawód dla Ciebie. W programowaniu nie ma przerwy, nie ma najmądrzejszych. Trzeba ciągle poszerzać swoją wiedzę i być na bieżąco. Jeżeli spoczniesz na laurach i zasiedzisz się w jednej technologii na 2 lata, to możesz być do tył względem rynku.

Jestem też sceptyczny, jeżeli chodzi o „szkoły programowania”. Mimo że być może dają efekty, to jest to strata kasy. Wydasz dużo kasy, nauczysz się technologii X, a za jakiś czas i tak będziesz musiał sam nauczyć się nowej technologii Y. Do tego trybu ciągłego poszerzania wiedzy trzeba się przyzwyczaić – inne dziedziny nauki nie są tak dynamiczne.

Uwaga! Wybierając kurs, wybierz ten najnowszy! Tak jak studia, tak jak całe programowanie, tak samo kursy i książki szybko się dezaktualizują. Przykładowo: ucząc się JavaScriptowej platformy Angular na dzień dzisiejszy musisz znaleźć kurs Angular2. Kursy Angular1 sprzed 2 lat są już totalnie zdezaktualizowane, a technologia jest martwa. Jeżeli wydasz pieniądze na książkę Angular1 (inna nazwa AngularJS), wydasz pieniądze w błoto. Co śmieszne, Angular1 i Angular2 nie są do siebie ani troszeczkę podobne, wiedzy z jednego nie da się wykorzystać w drugim (za pół roku pojawia się natomiast Angular5 :)) Potwierdza to konieczność ciągłego procesu nauki, od którego nie ma ucieczki.

Możesz raz nauczyć się fizyki lub matematyki, następnie uczyć tego przedmiotu w szkole średniej i sobie żyć. Nie da się natomiast raz nauczyć programowania i zasiąść w strefie komfortu w jakimś projekcie. Projekty informatyczne mają do siebie to, że ich czas życia jest dość krótki.

Wnioski

Wniosków po tym artykule może być wiele. Nie chcę, abyś pomyślał, że nie warto studiować! Chcę, abyś wiedział, że studia nie nauczą Cię programowania. Jeżeli chcesz programować zacznij sam, już teraz. Naucz się uczyć się samemu. Zainwestuj w dowolne, aktualne źródło wiedzy i ćwicz.

Studia nie są niezbędne do nauczenia się programowania. Deficyt na rynku jest tak duży, że programistami zostaje coraz więcej osób, które ze studiami informatycznymi nie mają kompletnie nic wspólnego.

]]>
https://www.p-programowanie.pl/studia-praca/studia-nie-ucza-programowania/feed/ 10
Czy matematyka jest potrzebna programiście? https://www.p-programowanie.pl/studia-praca/matematyka-potrzebna-programiscie/ https://www.p-programowanie.pl/studia-praca/matematyka-potrzebna-programiscie/#comments Fri, 25 Aug 2017 13:52:27 +0000 https://www.p-programowanie.pl/?p=3265 Pytanie o matematykę i programowanie jest jednym z najczęstszych pojawiających się na mojej skrzynce e-mailowej. Z tego powodu postanowiłem napisać kolejny artykuł, opisujący zależność pomiędzy matematyką a programowaniem. Jeżeli zastanawiasz się do czego matematyka jest potrzebna programiście, w tym artykule poznasz mój punkt widzenia na ten temat.

Związek pomiędzy matematyką i informatyką

Zaczynając ten artykuł należy wspomnieć, że informatyka jako dziedzina nauki wywodzi się bezpośrednio z matematyki. Dawniej stanowiła jej część, jednak przez jej niesamowicie szybki i daleko idący rozwój została uznana za osobną dziedzinę nauki. Informatyka z roku na rok ewoluuje a celem tego procesu jest szybsze osiąganie wyników za pomocą narzędzi jakie udostępnia.

Gdyby pokusić się o narysowanie piramidy informatyki, z całym przekonaniem jej podstawą byłaby właśnie matematyka. Z samej definicji wynika, że jest to dziedzina nauki wysoce abstrakcyjna, opierająca się na świecie cyfrowym.

Nauka programowania jest jak nauka matematyki

Z moich własnych obserwacji i doświadczeń wynika, że nauka programowania niczym nie różni się od nauki matematyki. Obydwie te nauki, jako że ze sobą bardzo związane, są naukami nienamacalnymi.

Ucząc się matematyki przechodzimy przez wszystkie klasy szkoły podstawowej, średniej i wyższej. Chyba każdy z nas poznał to uczucie, kiedy ucząc się podstaw matematyki w szkole podstawowej miał wrażenie, że nigdy nie będzie w stanie być lepszym i rozwiązywać trudniejszych zadań. Zadania ze studiów wyglądają wtedy jak magia, a uczeń czuje się przytłoczony ogromem materiału. Mimo tego niekomfortowego wrażenia lata edukacji mijają, a ludzie nie wiedząc kiedy stają się coraz lepsi. Zaczynając naukę od tabliczki mnożenia, docieramy do wielomianów, funkcji, szeregów, granic, całek, różniczek itd. Małymi krokami wszystko zaczyna się układać.

Tak samo przytłaczająca wydaje się nauka programowania. Zaczynając jego naukę od totalnych podstaw a więc podstawowych konstrukcji programistycznych, całość wydaje się bezsensowna. Programy pobierające i wyświetlające dane na czarnej konsoli wydają się niepotrzebne, a człowiek ma wrażenie, że nigdy nie będzie wstanie napisać skomplikowanego systemu. Tak jak w przypadku matematyki, jest to wrażenie złudne, ponieważ przez te początkowe etapy po prostu trzeba przejść.

Dlatego właśnie warto studiować

Podobieństwo w nauce matematyki i informatyki jest jednym z argumentów, dla których warto iść na studia informatyczne. Na studiach otrzymuje się odpowiednio poukładane porcje materiału, które podawane są odpowiedniej kolejności. Po 5 latach studiów i wielu pozornie nieprzydatnych przedmiotach, znacznie poszerza się nasz horyzont w dziedzinie informatyki. Nie tylko programowanie, nie tylko sieci informatyczne, nie tylko grafika inżynierska tylko całe fundamenty nauki jaką jest informatyka.

Nie idąc na studia nauka programowania może być przytłaczająca i ciężka. Nie mówię, że nie da się go nauczyć samemu, jednak przypomina to sytuację chęci nauczenia się matematyki ze studiów nie umiejąc tabliczki mnożenia ani ułamków. Człowiek pozostawiony sam sobie może mieć problemy z odpowiednim przejściem przez wszystkie etapy nauki, poprzez te najbardziej prymitywne (czyli pętle i wyświetlanie cyferek w czarnej konsoli) aż po bardziej złożone (kolorowe okienka, interfejsy użytkowników, wzorce projektowe, metodyki).

Dlatego programowanie jest trudne

Na koniec warto wspomnieć o interesującym fakcie, dlaczego programowanie wydaje się pozornie tak trudne? Odpowiedź jest oczywista: jego nauka jest jak nauka matematyki, a w Polsce jego nauczanie jest bardzo słabe. Jest to względnie nowa dziedzina nauki, a społeczeństwo nie nadąża z jego wprowadzeniem do szkół. Patrząc na ogromne zarobki programistów, globalną cyfryzację i wielki deficyt na rynku pracy oczywistym wydaje się, że programowanie powinno być przedmiotem szkolnym prowadzonym choćby w minimalnym wymiarze godzin – tak aby zaszczepić podstawy.

Matematyki uczymy się całe życie, pewnie około 15 lat. Programowania nie uczymy się nigdzie, wcale. Dlatego stając nagle przed decyzją „zaczynam uczyć się programowania„, człowiek staje przed ogromnym wyzwaniem opanowania potężnej dziedziny nauki, bez jakichkolwiek podstaw wiedzy. Wtedy nie wiadomo czego się łapać, a poznawanie podstaw wydaje się bezsensowne.

Gdzie ta matematyka w informatyce?

O tym, że informatyka jest powiązana z matematyką można dyskutować długo. Niektórzy się z tym zgadzają, inni stanowczo zaprzeczają. Ja jako programista skupię się oczywiście na matematyce w programowaniu. Na samym początku trzeba zrozumieć, że matematyka to nie tylko operacje algebraiczne, nie tylko analiza matematyczna. Wiele osób sądzi, że matematyki w programowaniu nie ma i wtedy zastanawiam się, czy oni chcieliby pisać kod całkami, żeby tę matematykę zobaczyć?

Dla mnie bardzo ciekawymi przedmiotami na studiach informatycznych były matematyka dyskretnametody optymalizacyjne. Gdybym miał zdefiniować matematykę dyskretną nazwałbym ją pomostem pomiędzy światem matematyki a programowania. Te dwa przedmioty są tak ściśle powiązane, że w zależności od specjalizacji nauczyciela akademickiego mogą bardziej przypominać zajęcia z programowania lub matematyki. Matma dyskretna jest nauką o zbiorach dyskretnych (nieciągłych). Jej przykładowe poddziały to:

  • logika matematyczna – bardzo fajne i przydatne. Poznaliśmy logikę Boolowską i jej prawa, prawa de Morgana, operacje na zdaniach, aksjomaty, tautologie, kwantyfikatory, implikacje, indukcje matematyczną, podstawy teorii mnogości, dopełnienia zbiorów itd. Na kolokwiach musieliśmy np. uprościć jakieś skomplikowane zdania używając praw z logiki Boola. Opiera się na nich cała elektrotechnika, służą między innymi do upraszczania obwodów elektronicznych i znajdują zastosowanie oczywiście w projektowaniu instrukcji warunkowych w programowaniu. Np. zgodnie z prawem zaprzeczenia koniunkcji instrukcję if !(p&&q) można zapisać jako if (!p || !q). Z rachunku logiki Boola korzysta się świadomie lub nieświadomie w całym życiu zawodowym, wykorzystywałem tę wiedzę także podczas programowania mikrokontrolerów AVR.
  • kryptografia – czyli jak działa szyfrowanie, jak działają szyfry jednostronne lub dwustronne. Jak to się dzieje, że asymetrycznego algorytmu RSA nie da się złamać i dlaczego są nim zabezpieczane np. komunikacje w grach MMORPG pomiędzy klientem a serwerem. Jak ważną rolę pełni brak wzoru na liczby pierwsze w algorytmach asymetrycznych i dlaczego cała kryptografia obecnie nam znana zawali się w momencie powstania komputerów kwantowych.
  • teoria gier – czyli jak program ma podejmować właściwe decyzje, dlaczego gra kółko krzyży jest grą o sumie stałej i dlaczego można za pomocą algorytmu minimax napisać robota komputerowego, który nigdy nie przegra. Dlaczego w szachach nie jest to już tak proste. Dylemat więźnia i problem wyboru obarczony stratą lub zyskiem.
  • teoria grafów – w informatyce po prostu magia. Często wykorzystywane tam, gdzie nigdy byś się tego nie spodziewał. M.in. problem znajdowania najkrótszej drogi w grach (algorytm Dijkstry), problem mostów, drzewa, reprezentacja problemów za pomocą grafów, problem najlepszego sąsiada. Nie wiem czy wszyscy wiedzą, ale dzięki teorii grafów mamy takie portale jak jakDojade.pl.

Czy czytając powyższe punkty jest sens się zastanawiać, czy matematyka jest związana z programowaniem? Opisałem bardzo ogólnie wyłączenie jeden przedmiot, którym jest matematyka dyskretna.

Być może, ktoś czytający ten artykuł chciałby poznać więcej relacji pomiędzy matematyką a programowaniem. Spróbujmy wypisać problemy programistyczne przypisane do działów matematyki:

  • operacje algebraiczne/analiza matematyczna  – pisanie wszelkiego rodzaju algorytmów, zwracanie uwagi na złożoność obliczeniową, raczej są to dedykowane i wyspecjalizowane aplikacje desktopowe. Może to być choćby program do obliczania maksymalnego naprężenia w prętach, który napisałem na 2 roku studiów jako forma zaliczenia mechaniki. Umiejętność odróżnienia algorytmów O(n^2) od O(ln(n)), rozumienie które warto optymalizować. Umieć zapisać funkcję matematyczną f(x) = x^2 jako funkcję w programie.
  • statystyka – w programowaniu raczej mało, ewentualnie zajmowanie się big data
  • optymalizacja – gdy pierwszy raz zetknąłem się z tym na studiach byłem zafascynowany. Związane z programowaniem liniowym i matematyką dyskretną. Przykładowo po poznaniu algorytmu optymalizacji liniowej simplex napisałem swój kalkulator dietetyczny dostępny pod adresem www.kalkulator-diety.pl. Jego zadaniem jest dobranie takich ilości produktów które wybiorę, aby odpowiadały mojej osobie (czyli automatyzuje układanie diety żywieniowej). Jest to też temat mojej pracy magisterskiej.
  • algorytmy genetyczne – stosunkowo nowość, mają tak szerokie zastosowanie, że aż Ciężko wszystko wypisać. Służą do zaprogramowania sztucznej inteligencji, przewidują pogodę, umieją grać na giełdzie forex. Mój współlokator używa ich do pisania aplikacji automatycznie układającej plan zajęć.
  • algebra liniowa – dużo tego jest w programowaniu gier komputerowych (nie używając silników graficznych wysokiego poziomu). Trzeba umieć obracać obiekty w przestrzeni, wszystko opiera się na macierzach i przekształceniach trygonometrycznych. Co najważniejsze trzeba robić to optymalnie, a nie metodą prób i błędów. Używane także we wszelkiego rodzaju obróbce grafiki (filtry, rozpoznawanie kształtów, analiza klatek, oprogramowanie do fotoradarów).

W obecnym projekcie używamy maszyny stanów (maszyny turinga) do definicji stanów dokumentów, też była omawiana na matematyce dysrketnej. Na koniec napiszę krótko: nie chcę mi się więcej wymyślać. Nie ma się co sprzeczać nad tym, że informatyka to matematyka, a w programowaniu też można jej spotkać całkiem sporo. Jeżeli ktoś sądzi, że w programowaniu nie ma matematyki, to zastanówmy się czego by się spodziewał? Pisania programu ułamkami? Przecież nie tylko na „liczbach” kończy się matematyka.

Jednak rozważmy dalej, czy można programować nie umiejąc matematyki?

Trzeba programować szybciej

Aby dopełnić ten artykuł i rozwiać wszystkim wszelkie wątpliwości trzeba przeanalizować jeszcze jeden ważny aspekt. Trzeba odpowiedzieć sobie na pytanie: dokąd zmierza informatyka a w szczególności programowanie? Programowanie jako ważny podzbiór informatyki rozwija się nieprawdopodobnie szybko. Praktycznie nie da się nadążyć za wszystkimi nowymi językami i platformami programistycznymi. Po co one powstają? Oczywiście po to, aby było szybciej i lepiej.

Przechodząc przez programowanie od najniższej warstwy (tzw. programowanie embedded), polegało ono na operowaniu instrukcjami procesora (czyli język assembler). Później powstał nieobiektowy język C, później jego starszy brat hybrydowy język C++. Mimo, że bardzo potężny wytwarzanie oprogramowania ciągle było zbyt wolne. Zaczęły powstać języki wysokiego poziomu czyli Java i C#. Programowanie stało się teraz ultra szybkie. Co będzie dalej? W między czasie modny jest JavaScript, ponieważ jest totalnie niezależny od platformy na jakiej się wykonuje. Do JavaScripta powstało wiele platform takich jak AngularJSReactBackboneKnockout.js. Wszystko po to aby było.. szybciej! Stworzony został serwer Node wraz z setkami tysięcy gotowych modułów (sławny node_modules, który często ważą 50GB, czyli 1000 razy więcej niż sam projekt).

Wszystkim zależy na czasie, programowanie rozwija się w stronę szybkiego wytwarzania kodu. Programiści są drodzy, a pisanie systemów informatycznych trwa bardzo długo. Pisanie programów „wolno” nie ma sensu, ponieważ zanim powstaną już stają się przeterminowane. W tym momencie pojawiają się określenia tzw. programisty oraz dewelopera.

Programista a deweloper

Na różnicę pomiędzy programistą a deweloperem nakierował mnie kiedyś wykładowca od mechaniki. Na zajęciach wywiązała się ciekawa dyskusja, na temat różnicy pomiędzy jednym a drugim. Po krótszej chwili doszliśmy do wniosku, że programista to osoba odpowiedzialna typowo za klepanie kodu. Za napisanie algorytmu, który ma działać tak i tak, który ma pobrać jakieś dane i zwrócić wynik. Programista nie zajmuje się niczym więcej. Fajnie gdyby programista miał wysokie umiejętności matematyczne w momencie gdy pisze bibliotekę, która będzie odpowiedzialna np. za przeprowadzanie obliczeń numerycznych.

Z drugiej strony deweloper (ang. software developer) jest osobą, na którą trzeba spojrzeć nieco szerzej. Jest on odpowiedzialny za większą koncepcję, ma wymyślić sposób, umieć połączyć kilka technologii lub kilka rozwiązań. Dla programisty z krwi i z kości ważne mogą okazać się umiejętności matematyczne/fizyczne/mechaniczne lub inne, w zależności w jakim projekcie pracuje. Dla dewelopera ważniejsze może się okazać znanie wielu technologii, umiejętność wykorzystania gotowych rozwiązań i bibliotek dostarczonych przez programistów.

Gdzieś kiedyś usłyszałem, że programista opracowuje skomplikowane biblioteki, które mają działać najszybciej jak to możliwe, a deweloper ma umieć z tych bibliotek korzystać. I tak np. pisząc gry deweloperzy korzystają z gotowych silników graficznych, którą są napisanie idealnie pod względem wydajności, i nie jest im do tego potrzebna matematyka. Z drugiej strony, nad silnikiem graficznym musieli pracować programiści, który na pewno nie mięli problemów z algebrą liniową.

Podział na programistę i dewelopera nie jest moim wymysłem. Choć w naszym kraju nie jest ten podział zbytnio respektowany, to jednak odwrotnie jest w USA. Tam stanowisko programisty (ang. programmer) a stanowisko dewelopera (ang. software developer) jest po prostu inaczej definiowane, te osoby odpowiedzialne są za inne rzeczy.

Czy można programować nie umiejąc matematyki

Każdy medal ma dwie strony. Mimo, że matematyka jest przydatna oczywiście można programować bez niej. Przede wszystkim matematyka nie jest potrzebna front-endowcom (ang. front-end developer). Tworzenie szablonów, pisanie HTMLa lub reguł CSS nie wymagają żadnych skomplikowanych obliczeń. Głębszy front-end czyli programowanie w jakimś frameworku JavaScript także nie będzie od nas wymagać matematyki. Tworząc aplikację w Angular, React lub czystym JavaScript potrzebne dane najpewniej zostaną dostarczone przez back-end.

Podsumowując artykuł powiedzmy otwarcie, matematyka niezbędna programiście (deweloperowi) nie jest. Można naprawdę dużo zarabiać i nie używać matematyki. Natomiast nie postrzegajmy matematyki jako tylko i wyłączenie obliczeń algebraicznych, bo ich jest stosunkowo mało. Patrzmy na nią jako zdolność analitycznego i abstrakcyjnego myślenia, która zawsze może przydać się projektując skomplikowane fragmenty systemu. Błyskotliwość, szybkość znajdywania rozwiązań są cechami pożądanymi w naszym zawodzie. Oto kilka ważnych wniosków:

  • w informatyce i programowaniu jest bardzo dużo matematyki – jest to po prostu dziedzina związana z matematyką i bazująca na matematyce, jeżeli sądzisz inaczej to jesteś ignorantem
  • matematyka to nie tylko ułamki i całki – szczególnie w kontekście programowania. Doskonałym przykładem jest matematyka dyskretna, która wprowadza w świat pomiędzy matematyką a programowaniem, uczy logicznego myślenia, ukazuje mechanizmy które warto znać i umieć z nich korzystać
  • można być dobrym matematykiem i nie umieć programować – no wiadomo, tak się zdarza, znam realne przypadki (nie znam na to wytłumaczenia)
  • można być dobrym deweloperem i nie umieć matematyki – można zajmować się np. aplikacjami internetowymi, nie pakować się w problemy, których nie umiemy rozwiązać (czyli dedykowane aplikacje bazujące na skomplikowanych obliczeniach)
  • nie można być dobrym programistą i nie umieć matematyki – ale chyba wszystko zależy od kontekstu, jak rozumiemy różnicę pomiędzy programistą a deweloperem i czy taką granicę w ogóle uznajemy. No nie oszukujemy się, będąc matematycznym zerem silnika do gry nie napiszemy, symulacji fizycznej nie napiszemy, biblioteki do obliczeń metodą MES nie napiszemy, biblioteki rozwiązującej całki metodą Newtona-Cotesa nie napiszemy. Możemy co najwyżej, jako deweloper, wykorzystać istniejące.
  • wielu sławnych programistów, którzy są nieprzeciętnie dobrzy, są z wykształcenia matematykami
  • bardziej potrzebni są deweloperzy niż programiści – jest mało projektów, które wymagają wysokich zdolności matematycznych. Nikt nie zatrudni matematycznego zera do pisania takiej aplikacji jak MatLab, jednak powiedzmy sobie szczerze takich stanowisk jest mniej. IT stoi mocno na aplikacjach bankowych, systemach z rodziny ERP służących do zarządzania firmami itp. Tam matematyki nie ma. Jeżeli ktoś mądry napisał bibliotekę do obsługi formatu Mp3 (dzwięk w formacie mp3 zapisany jest w postaci szeregu furiera), to każdy inny program będzie z niej korzystał. Nikt nie wprowadza swoich kodowań audio, tylko korzysta z istniejących i sprawdzonych.

Na sam koniec warto jeszcze wspomnieć, że umiejętności matematyczne mogą nam się przydać podczas szukania pracy. Wierzcie lub nie, jednak na Krakowskim rynku IT są firmy, które na rozmowie rekrutacyjnej dają zadania z czystej matematyki i każą je rozwiązać na kartce. Na szczęście takie firmy to odosobnione przypadki.

]]>
https://www.p-programowanie.pl/studia-praca/matematyka-potrzebna-programiscie/feed/ 2
Fabryka abstrakcyjna – robisz to źle https://www.p-programowanie.pl/wzorce-projektowe/fabryka-abstrakcyjna/ https://www.p-programowanie.pl/wzorce-projektowe/fabryka-abstrakcyjna/#comments Fri, 11 Aug 2017 23:53:19 +0000 https://www.p-programowanie.pl/?p=3197 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).
  • 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:

IManageArea manageArea;
    Employee employee = employeeService.Load("Managment", 42);

    if (employee.isManager)
    {
        manageArea = new GlobalManageArea();
    }
    else
    {
        manageArea = new LocalManageArea();
    }

    manageArea.FireEmployees(1000);
    // itd

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, 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:

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.

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:

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.

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:

// 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()
    }

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 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 dziedziczeniabrak 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ą.

]]>
https://www.p-programowanie.pl/wzorce-projektowe/fabryka-abstrakcyjna/feed/ 5
Po co testy jednostkowe? https://www.p-programowanie.pl/paradygmaty-programowania/po-co-testy-jednostkowe/ https://www.p-programowanie.pl/paradygmaty-programowania/po-co-testy-jednostkowe/#comments Sat, 22 Apr 2017 20:11:46 +0000 https://www.p-programowanie.pl/?p=3168 Wielu programistów ma bardzo różne podejście do testów jednostkowych. Niektórzy piszą, bo muszą. Inni nie lubią lub nie rozumieją, trzymają się z daleka. Co tak naprawdę dają nam testy jednostkowe i czy warto zaprzątać sobie nimi głowę?

Czym są testy jednostkowe?

Testy jednostkowe to metoda testowania wytwarzanego oprogramowania, polegająca na pisaniu metod testujących określone, małe fragmenty naszego programu (jednostki). Jednostkami mogą być np. metody lub klasy. Kończąc studia informatyczne lub zaczynając przygodę z programowaniem ciężko dostrzec zalety pisania testów jednostkowych. Wszystko wydaje się niby proste, jednak nasuwa się pytanie: po co testować coś, co sami piszemy? Brzmi to paradoksalnie jak np. sprawdzanie poprawności zadania matematycznego rozwiązanego przez nas samych.

Dodatkowym zniechęceniem do pisania testów mogą okazać się cechy testów jednostkowych takie jak:

  • żadnego oprogramowania nie jesteśmy przetestować całkowicie
  • nigdy nie mamy pewności znalezienia wszystkich błędów, nawet gdyby pokrycie testami wynosiło 100% (co jest nierealne)
  • testy wydłużają czas wytwarzania oprogramowania praktycznie nawet o połowę

Po tych wszystkich rozważaniach nasuwa się pytanie: po co?

KIedy warto testować

Przede wszystkim należy uzmysłowić sobie kiedy warto pisać testy jednostkowe do aplikacji. Jeżeli piszemy prosty programik i mamy pewność, że będziemy rozwijać go samemu, to testy raczej nie będą potrzebne. Nie będą potrzebne także w przypadku programów, które chcemy napisać
„raz i zostawić”, nie nastawiamy się na ich systematyczny rozwój.

Duże projekty i wielu programistów

Całkiem inaczej w dużych projektach pisanych przez kilka osób. Tworzenie dużego projektu niekiedy może przypominać spagetti. Każdy programista dopisuje do projektu swoje własne metody i klasy, rozwija istniejące funkcje i dodaje nowe. W takim przypadku brak testów może w przyszłości nieść wiele negatywnych skutków.

Każdy programista ma swój styl kodowania, który dodatkowo zmienia się pod wpływem czasu i doświadczenia. Jeden kod napisany przed dwóch różnych programistów, działający tak samo, będzie prawdopodobnie wyglądał inaczej. Przykładem może być proste przekazywanie argumentów do funkcji.

Odmienne style pisania kodu

Wyobraź sobie sytuację, w której musisz napisać funkcję wypisującą na ekran informacje o osobie. Jeden programista zadeklaruje ją następująco: void PrintPersonInfo(Person person) a inny tak: void PrintPersonInfo(string name, string lastname, int age). Niby to samo, a jednak nie. Oczywiście poprawnym wyborem jest deklaracja druga. Nie wpływa ona negatywnie na wartość metryki Coupling Between Objects z zestawu metryk CK. Metryka CBO mierzy stopień sprzężenia pomiędzy klasami i powinna być ona jak najmniejsza (typy proste nie zwiększają wartości wskaźnika).

Będąc programistą dużego projektu, musisz wiedzieć, że spotkasz się z sytuacjami takimi jak powyższa. Wynika to z różnego poziomu doświadczenia współpracowników a także choćby z ich języków bazowych z jakich się wywodzą. Uogólnienie parametru funkcji poprzez typ złożony nie będzie niczym dziwnym dla JavaScriptowca, jednak programista języków silnie typowanych np. C# powinien tego unikać.

Kiedy ktoś użyje typów prostych raczej nic się nie wysypie pod wpływem czasu. Jeżeli ktoś użyje typu złożonego takiego jak klasa Person, to nikt tak naprawdę nie wie, jak ta klasa będzie wyglądać za 4 miesiące lub za rok. Jeżeli na dodatek będzie ona wykorzystana jako model warstwy biznesowej można być pewnym bugów.

W takich momentach niezbędne są testy. Współpracując z wieloma osobami nie jesteśmy w stanie wpływać na jakość ich kodu. Testy jednostkowe gwarantują nam to, że nie musimy. W minimalnym stopniu chronią nas one przed zmianami, które zajdą w projekcie za wiele miesięcy lub lat. Mimo, że my o czymś zapomnimy a inny programista nie będzie wiedział, że element systemu, który reużywa jest używany gdzieś indziej, to testy będą pamiętać o tym za nas.

Jakie ma być pokrycie kodu testami?

Pisanie testów wydłuża proces tworzenia oprogramowania. M.in. z tego powodu nie jest realne aby projekt był w 100% pokryty testami. Jest to niepotrzebne, wręcz może prowadzić do błędów. To ile testów napisać zależy od czasu jakim dysponujemy i przede wszystkim od przeczucia. Warto testować fragmenty kodu, które są bardzo newralgiczne i używane w wielu miejscach.

Podobno dobre pokrycie testami to 40%, jednak czy ta informacje coś mówi? Można mieć duże pokrycie testami a żadnych korzyści, lub małe pokrycie testami i wielkie korzyści. Zależy to od jakości testów i elementów, które testujemy.

Testowanie regresyjne

W jednym z projektów (ERP) jaki współtworzyłem nie pisaliśmy testów jednostkowych. Cały projekt w miarę działał dopóki nie pojawiła się magiczna funkcja kolejki zadań. Kolejka była fragmentem systemu działającym dosłownie wszędzie. Każda nowa funkcja, która się pojawiała, w jakiejś części musiała zintegrować się z kolejką.

Pomijając fakt, że kolejka była źle zaprojektowana przez programistę, który już nie pracował, nie dało się jej nawet dotknąć. Jedna zmiana wysypywała stare funkcje systemu przetestowane wiele miesięcy wcześniej. Nie dało się jej nawet dotknąć w znaczeniu jak najbardziej dosłownym. Dopisanie nowego parametru lub dodanie nowego typu zadania, psuło funkcje systemu zaimplementowane 8 miesięcy temu.

Pomyślisz „no dobrze, nie ma testów jednostkowych więc trzeba testować ręcznie?” – otóż nie. Nie da się przetestować wszystkich scenariuszy systemu tworzonego latami przez kilku/kilkunastu programistów. W tak dużym i złożonym systemie dopisanie jednej funkcji „do kolejki” wymagałoby 2 dni testowania aplikacji. Drobna poprawa funkcji? Kolejne dwa dni testowania. Dopisujemy nową funkcję? Kolejne kilka dni testowania. Tak wyglądający proces testowania oprogramowania jest kosmiczny.

Lekarstwem byłyby tutaj testy jednostkowe. Ich pokrycie mogłoby wynosić chociaż 8% projektu. Ważne, aby pokryta była bardzo wrażliwa funkcja systemu, z której korzysta wiele elementów systemu. Zmieniając lub dopisując coś po wielu miesiącach wystarczyłoby odpalić testy, aby przekonać się czy nie zepsuliśmy kogoś pracy.

Testy wymagają pisania przemyślanego kodu

Kolejną zaletą testów jednostkowych jest pisanie mądrego kodu. Na studiach lub świeżo po studiach nic na ten temat nie wiadomo, jednak po kilku miesiącach pracy zawodowej zaczyna się poznawać wiedzę tajemną. Otóż, nie wszystkie fragmenty kodu da się pokryć testami. Aby dało się napisać test jednostkowy, kod programu musi być testowalny.

Przykładem platformy programistycznej, która umożliwia pisanie nietestowalnego kodu jest ASP.NET MVC. Nie wiem jak będzie wyglądać sytuacja w .NET Core, jednak MVC w żaden sposób nie wymusza na programiście eleganckich rozwiązań. Jeżeli nie pomyślisz, lub ktoś w zespole nie pomyśli, i nie wprowadzi do projektu wzorca repozytorium z jakimś mądrym mechanizmem Inversion of Control (czyli odwrócenia zależności np. dependency injection) to wasz kod będzie absolutnie nietestowalny. Jeżeli ktoś zacznie używać klas statycznych, pisać tzw. klasy helperki czyli niby do wszystkiego, niby do niczego, to wasz kod będzie absolutnie nietestowalny.

Każdy obiekt, z którego chcesz skorzystać, powinien zostać w jakiś sposób wstrzyknięty. Powinien spełniać jakiś interfejs i być od niego zależny (zasady SOLID). Dzięki interfejsom można napisać test i zamockować np. obiekt bazy danych, lub warstwę repozytorium.

Miłym zaskoczeniem było dla mnie tutaj zetknięcie się z platformą AngularJS, w której czy chcesz, czy nie chcesz, musisz korzystać z mechanizmu wstrzykiwania zależności. Idąc dalej, praktycznie zawsze jesteś w stanie przetestować swoje serwisy i kontrolery, co np. nie uda się w ASP.NET MVC.

Jeżeli w projekcie macie testy, to programista pisząc bubla szybko zorientuje się, że tak to działać nie może, ponieważ nie da się tego przetestować. Wymusi to na nim konieczność refaktoryzacji lub ponownego zaimplementowania funkcji.

Szkoda czasu na testy? Oj nie..

Jest to najpiękniejszy argument za pisaniem testów jednostkowych, z którym spotkałem się na szkoleniu z TDD, oraz odczułem jego skutki na własnej skórze. Możesz myśleć, że szkoda Ci czasu na pisanie testów, jednak cały ten czas zwróci się z nawiązką.

Zadanie, które możesz napisać w 5h z testami napiszesz bez testów w 3h. Pozornie zaoszczędziłeś 2h pracy, jednak napisałeś nietestowanego/nietestowalnego bubla. Teraz każdy inny programista, który będzie musiał w jakiś sposób skorzystać z Twoich metod, klas, modułów, będzie musiał przeprowadzać testy ręczne. Będzie musiał linijka po linijce starać zrozumieć się, dlaczego w 97 linijce jest jakaś dziwna instrukcja warunkowa, sprawdzająca czy zmienna priceDiff jest równa zero, i dlaczego jak jest równa zero to wyskakujesz z obiektu pętli. Jeżeli po jakimś czasie da radę się domyślić – pół biedy. Jeżeli nie da rady się domyślić a projekt skompiluje się bez błędów, to ktoś i tak zmarnuje czas szukając później błędów w działaniu produkcji.

Brak testów to brak możliwości refaktoryzacji

Niezależnie od poziomu naszej naszej wiedzy oraz od naszego doświadczenia, każdy każdy programista ma świadomość konieczności systematycznej refaktoryzacji pisanego kodu. Refaktoryzacja i optymalizacja są tematami tak obszernymi, że należałoby o nich napisać osobny artykuł.

Warto jednak brać pod uwagę, że refaktoryzacja w dużym projekcie, który nie ma testów, jest po prostu całkowicie niemożliwa. Bez testów programista może sobie pozwolić najwyżej na pozmienianie nazw zmiennych, ponieważ jakiekolwiek próby rozbicia klas na osobne podklasy prawdopodobnie zakończą się powstaniem błędów.

Podsumowanie

W każdym z wypadków wiele czasu da się oszczędzić świadomie pisząc testy jednostkowe dla odpowiednich elementów systemu. Nie chodzi tu o to, aby starać się być z testami w granicach 30-40% pokrycia. To tylko wskaźnik, który może mówić wszystko albo nic. W sztuce pisania testów należy wiedzieć co testować i gdzie testy będą niezbędne, a gdzie okażą się stratą czasu.

Czy pisanie testów wydłuża proces pisania oprogramowania? – zdecydowanie tak, dlatego testy trzeba pisać mądrze, pokrywać testami tylko wrażliwe elementy systemu.

Po co testować coś co sami napisaliśmy? Nie piszemy testu, aby przetestować własny kod. Testy piszemy na zapas, to jak inwestycja na przyszłość. Test nie ma na celu wychwycenia naszych błędów, tylko ewentualne błędy podczas przyszłej refaktoryzacji (szczególnie jeżeli wykona ją ktoś inny niż my). Jeżeli tworzymy jakąś funkcję np. kalkulator walut, zakładamy że jesteśmy jedyną osobą, która ma wiedzieć jak ma ten kalkulator działać. Inni nie wiedzą, dlaczego trzeba pokryć go testami.

Pisanie testów zawsze zaprocentuje w przyszłości, chyba że piszemy testy „sztuka dla sztuki”, pokrywamy testami niepotrzebne elementy.

]]>
https://www.p-programowanie.pl/paradygmaty-programowania/po-co-testy-jednostkowe/feed/ 7
Filtry https://www.p-programowanie.pl/kurs-angular/filtry/ https://www.p-programowanie.pl/kurs-angular/filtry/#respond Sat, 25 Mar 2017 23:11:13 +0000 http://www.p-programowanie.pl/?p=3014 Filtry w Angularze są bardzo przydatnym narzędziem. Pomagają formatować dane i są wygodnym sposobem na reużywalność kodu. Tworząc aplikację w AngularJS mamy dostępnych wiele gotowych filtrów. Jeżeli nie spełnią one naszych oczekiwań, możliwe jest dopisanie swoich. Ich główną zaletą w stosunku do zwykłych funkcji jest to, że możemy ich używać zarówno po stronie kontrolerów jak i w widokach. Sprytne operowanie filtrami potrafi znacznie skrócić kod aplikacji.

Użycie filtrów w widoku

Używanie wbudowanych filtrów jest bardzo proste. Filtr możemy dokleić do dowolnego wyrażenia, oraz niektórych dyrektyw. Przykładowe użycie wygląda następująco:
h
{{ wyrażenie | <nazwa_filtru> }}

Po wywołaniu powyższego kodu podczas wywołania wyrażenia zostanie dla niego zastosowany filtr. Filtry łączymy za pomocą operatora pionowej kreski (tzw. pipe).  Filtry możemy ze sobą łączyć, są one wywoływane w kolejności od lewej do prawej:

{{ wyrażenie | <filtr1> | <filtr2> | <filtr3> }}

Co ciekawe, niektóre filtry mogą przyjmować argumenty. Przekazujemy je do filtra po dwukropku.

{{ wyrażenie | <filtr1> | <filtr2>:<arg1> | <filtr3>:<arg1>:<arg2> }}

Lista dostępnych filtrów

Dla wygody programista AngularJS udostępnia wiele wbudowanych filtrów. Oto ich lista:

Filter Opis
currency Formatuje liczby do postaci waluty
date Formatuje datę według wzorca
filter Filtruje elementy kolekcję
json Wyświetla obiekt jako tekst JSON
limitTo Przycina tablice lub tekst do określonej długości
lowercase Zamienia tekst na małe litery
number Formatuje liczbę według wzorca
orderBy Sortuje po określonym parametrze
uppercase Zamienia tekst na duże litery

Przykład użycia currency

Currency jest bardzo wygodnym filtrem do formatowania walut. Jako jeden z wielu filtrów korzysta z tzw. locale z biblioteki i18n. Jeżeli dołączymy odpowiedni plik locale do projektu (np. z konfiguracją polską) wtedy domyślną walutą filtra będzie polski Złoty. Jeżeli nie dołączymy ustawień lokalizacji domyślną walutą będzie amerykański Dolar. Oto przykładowy kod:

<script>
	var app = angular.module("mainModule", []);
 
	app.controller('simpleCtrl', simpleController);
	
	function simpleController($scope) {
		$scope.kwota = 2500.25;
	}
</script>
 
<body ng-app="mainModule" ng-controller="simpleCtrl">
	<p>{{kwota | currency}}</p>
	<p>{{kwota | currency:'PLN'}}</p>
	<p>{{kwota | currency:''}} zł</p>
	<p>{{ 1234 | currency:''}} zł
</body>

Efekt działania jest następujący:

Zauważ jaka duża jest różnorodność osiągniętych wyników. W pierwszym przypadku wykorzystane zostały ustawienia domyślne. W drugim przypadku zmieniliśmy symbol waluty przekazując wprost parametr PLN.

Domyślnie znak dolara wyświetlany jest przed kwotą. Chcąc wyświetlić polską walutę  za liczbą, możemy posłużyć się sztuczką. Przekazujemy pusty parametr waluty a interesującą nas walutę dopisujemy bezpośrednio poza wyrażeniem. Innym lepszym rozwiązaniem byłoby dołączenie odpowiedniej konfiguracji biblioteki i18n przeznaczonej dla Polski.

Przykład użycia filter

Filter o nazwie filter służy do odfiltrowywania elementów kolekcji o określonych parametrach. Łączymy go z dyrektywą ng-repeat. Ma naprawdę sporo możliwości. Z jego pomocą łatwo można zrobić Angularową wyszukiwarkę. Oto przykładowy kod:

<script>
	var app = angular.module("mainModule", []);
 
	app.controller('simpleCtrl', simpleController);
	
	function simpleController($scope) {

		$scope.pracownicy = [
			{id: 1, imie: "Karol", wiek: 24},
			{id: 2, imie: "Kaja", wiek: 51},
			{id: 3, imie: "Tomek", wiek: 22},
			{id: 4, imie: "Justyna", wiek: 39},
			{id: 5, imie: "Kazek", wiek: 8}
		]
	}
</script>
 
<body ng-app="mainModule" ng-controller="simpleCtrl">
	<p>Szukaj: <input type="text" ng-model="szukaj"></p>
	<table>
		<tr ng-repeat="pracownik in pracownicy | filter : szukaj track by pracownik.id">
			<td>{{pracownik.imie}}<td>
			<td>{{pracownik.wiek}} lat</td>
		</tr>
	</table>
</body>

Wynik działania jest następujący:

Pole wyszukiwania zbindowane jest bezpośrednio ze zmienną szukaj. Jej deklaracja nie była potrzebna w $scope ponieważ odbędzie się automatycznie. Do dyrektywy ng-repeat został natomiast dołączony filter. Filtruje on wartości właśnie po zmiennej szukaj. Zwróć uwagę, ponieważ filtry wykonują się od lewej do prawej dlatego track by musiał zostać dołączony za filtrem, aby indeksować elementy przefiltrowane a nie te, które filtr wytnie.

Filtrowanie odbywa się po wszystkich atrybutach obiektów kolekcji pracownicy. Z tego powodu wpisanie wartości 5 wyświetliło nam Kazimierza, który nie ma 5 lat – ma natomiast atrybut id z taką wartością.

Możemy filtrować kolekcję tylko po jednej wartości przesyłając do filter obiekt z nazwą atrybutu:

<tr ng-repeat="pracownik in pracownicy | filter : {imie: szukaj} track by pracownik.id">
	<td>{{pracownik.imie}}<td>
	<td>{{pracownik.wiek}} lat</td>
</tr>

Takie ustawienie spowoduje, że wyszukiwane będą tylko elementy, których atrybut imie dopasowuje się do zmiennej szukaj.

Przykład użycia json

Za pomocą filtra json możemy w bardzo prosty sposób wyświetlić dowolny obiekt JavaScript podpięty do naszego $scope. Przykładowy kod:

<script>
	var app = angular.module("mainModule", []);
 
	app.controller('simpleCtrl', simpleController);
	
	function simpleController($scope) {
	
		$scope.osoba = {
			imie: "Karol",
			wiek: 24,
			ulubionePiosenki: ['aaa', 'bbb', 'ccc']
		}
	}
</script>
 
<body ng-app="mainModule" ng-controller="simpleCtrl">
	<pre>{{osoba | json}}</pre>
</body>

Efekt działania:

Przykład użycia orderBy

Filter orderBy jest niezwykle pomocnym filtrem, używanym do budowania zaawansowanych list. Możemy zastosować go do dyrektywy ng-repeat i bardzo dobrze łączy się z on z filter filter. Używając go możemy ustalić po jakim atrybucie przefiltrować kolekcję:

<script>
	var app = angular.module("mainModule", []);
 
	app.controller('simpleCtrl', simpleController);
	
	function simpleController($scope) {
	
		$scope.atrybutSortowania = 'id';
	
		$scope.osoby = [
			{id: 1, imie: "Karol", wiek: 24},
			{id: 2, imie: "Zenon", wiek: 12},
			{id: 3, imie: "Maciej", wiek: 33},
			{id: 4, imie: "Beata", wiek: 12}
		];
		
		$scope.sortuj = function(atrybut) {
			$scope.atrybutSortowania = atrybut;
		}
	}
</script>
 
<body ng-app="mainModule" ng-controller="simpleCtrl">
	<table>
		<tr>
			<th ng-click="sortuj('id')">id</th>
			<th ng-click="sortuj('imie')">imie</th>
			<th ng-click="sortuj('wiek')">wiek</th>
		</tr>
		<tr ng-repeat="osoba in osoby | orderBy: atrybutSortowania track by osoba.id">
			<td>{{osoba.id}}</td>
			<td>{{osoba.imie}}</td>
			<td>{{osoba.wiek}}</td>
		</tr>
	</table>
</body>

Efekt działania:

Kod jest rozbudowany, ale bardzo prosty. Filter orderBy przyjmuje jako parametr nazwę atrybutu kolekcji, po której ma sortować. Jeżeli parametr nie zostanie przekazany jako string wtedy szuka on funkcji lub zmiennej podpiętej do $scope o takiej nazwie. W naszym przypadku dodana została funkcja, ustawiająca parametr sortujący na właściwą wartość. Sortowanie odbywa się po parametrze sortującym.

Łącząc filtr orderBy z filter filter można uzyskać zaawansowaną tabelkę z możliwością sortowania kolumn i dynamicznego wyszukiwania. Dokładając do nich filter limitTo można stworzyć paginację wartości np. po 50 elementów w tabelce na stronę.

Użycie filtrów w kontrolerze

Filtrów Angularowych można użyć także w kodzie kontrolera aplikacji. W tym celu należy skorzystać z serwisu o nazwie $filter. Zwraca on instancję filtra o nazwie przekazanej jako argument. Aby móc użyć serwisu należy wstrzyknąć go jako zależność do kontrolera. Przykładowy kod wygląda następująco:

<script>
	var app = angular.module("mainModule", []);
 
	app.controller('simpleCtrl', simpleController);
	
	function simpleController($scope, $filter) {
	
		var kwota = 123456.60;

		$scope.kwotaFormatowana = $filter('currency')(kwota);
		
		$scope.kwotaFormatowana2 = $filter('currency')(kwota, '');
		
	}
</script>
 
<body ng-app="mainModule" ng-controller="simpleCtrl">
	<p>{{kwotaFormatowana}}</p>
	<p>{{kwotaFormatowana2}} zł</p>
</body>

Efekt działania:

Oprócz wykorzystania filtru, mamy możliwość przesyłana do niego parametrów co widać w przykładzie wyżej. Wyczyszczony został domyślny symbol waluty a następnie ręcznie w widoku dopisana waluta .

Własne filtry

Platforma AngularJS pozwala na pisanie własnych filtrów. Nie jest problemem napisanie własnej funkcji, która filtruje tablicę po określonej wartości atrybutu, jednak dzięki wprowadzeniu filtrów kod aplikacji Angular jest uporządkowany. Filtry znajdują się w filtrach, kontrolery w kontrolerach a komponenty w komponentach.

Z tego powodu zachęcam Cię, do trzymania funkcji odpowiedzialnych za filtrowanie tablic właśnie w postaci filtrów.

Aby utworzyć własny filter należy skorzystać z funkcji konstruującej wywołanej na instancji modułu. Przykładowo:

<script>
	var app = angular.module("mainModule", []);
 
	app.filter('mojFilter', function() {
		//cialo filtra
	});
</script>

Tak samo jak w przypadku kontrolerów, możemy stworzyć filter jako funkcję anonimową lub normalną:

<script>
	var app = angular.module("mainModule", []);
 
	app.filter('mojFilter', function() {
		//anonimowy
	});
	
	app.filter('mojFilter2');
	function mojFilter2() {
		//normalny
	}
</script>

Przykładowy własny filter

Stwórzmy własny filter Angularowy wyświetlający tylko ludzi wysokich. Ważną cechą filtra musi być jego skalowalność, a więc możliwość dynamicznej edycji wzrostu progowego. Kodem początkowym niech będzie następujący prosty kod:

<script>
	var app = angular.module("mainModule", []);
 
	app.controller('simpleCtrl', simpleController);
	
	function simpleController($scope, $filter) {
		$scope.osoby = [
			{id: 1, imie: "Karol", wzrost: 180},
			{id: 2, imie: "Arek", wzrost: 178},
			{id: 3, imie: "Natalia", wzrost: 172},
			{id: 4, imie: "Przemek", wzrost: 192},
		];
	}
	
</script>
 
<body ng-app="mainModule" ng-controller="simpleCtrl">
	<table>
		<tr ng-repeat="osoba in osoby track by osoba.id">
			<td>{{osoba.imie}}</td>
			<td>{{osoba.wzrost}}cm</td>
		</tr>
	</table>
</body>

Aby dodać filter należy:

  • zdefiniować nazwę filtra poprzez funkcję konstruującą <moduł>.filter(<nazwa_filtra>).
  • podpiąć do filtra funkcję filtra, która zwróci anonimową funckję filtrującą
  • anonimowa metoda filtra powinna przyjmować parametry kolekcji wejściowej oraz kryterium filtrowania
  • anonimowa metoda filtra powinna zwrócić przefiltrowaną tablicę wyników

Metoda filtra może w naszym wypadku wyglądać następująco:

function wysocyLudzieFilter() {
	return function(input, wzrostProgowy) {
		var przefiltrowane = [];
		
		//jezeli ktos nie podał parametru to przyjmij 180cm
		if (typeof wzrostProgowy === "undefined") {
			wzrostProgowy = 180;
		}
		
		for (var i = 0; i<input.length; i++) {
			if (input[i].wzrost >= wzrostProgowy) { //jeżeli spełnia warunek wzrostu
				przefiltrowane.push(input[i]) //to dodaj do nowej tablicy
			}
		}
		
		return przefiltrowane; //zwróć nową tablicę
	}
};

Pierwszym parametrem input jest kolekcja wejściowa, zostanie ona przesłana do filtra automatycznie. Kolejne argumenty to dodatkowe parametry filtra, które możemy zdefiniować. W powyższym kodzie jest to wzrostProgowy powyżej którego człowiek jest uznawany za wysokiego. Jeżeli nie zostanie on przekazany do filtra, wtedy przyjmijmy że jego wartość wynosi 180.

Po podpięciu funkcji do instancji filtra należy zastosować go w dyrektywie ng-repeat:

<script>
	var app = angular.module("mainModule", []);
 
	app.controller('simpleCtrl', simpleController);
	app.filter('wysocyLudzieFilter', wysocyLudzieFilter);
	
	
	function simpleController($scope, $filter) {
		$scope.wzrostMin = 180;
		
		$scope.osoby = [
			{id: 1, imie: "Karol", wzrost: 180},
			{id: 2, imie: "Arek", wzrost: 178},
			{id: 3, imie: "Natalia", wzrost: 172},
			{id: 4, imie: "Przemek", wzrost: 192},
		];
	}
	
function wysocyLudzieFilter() {
	return function(input, wzrostProgowy) {
		var przefiltrowane = [];
		
		//jezeli ktos nie podał parametru to przyjmij 180cm
		if (typeof wzrostProgowy === "undefined") {
			wzrostProgowy = 180;
		}
		
		for (var i = 0; i<input.length; i++) {
			if (input[i].wzrost >= wzrostProgowy) { //jeżeli spełnia warunek wzrostu
				przefiltrowane.push(input[i]) //to dodaj do nowej tablicy
			}
		}
		
		return przefiltrowane; //zwróć nową tablicę
	}
};
	
</script>
 
<body ng-app="mainModule" ng-controller="simpleCtrl">
	<input type="number" ng-model="wzrostMin" /> <br><br>
	<table>
		<tr ng-repeat="osoba in osoby | wysocyLudzieFilter:wzrostMin track by osoba.id">
			<td>{{osoba.imie}}</td>
			<td>{{osoba.wzrost}}cm</td>
		</tr>
	</table>
</body>

Efekt działania:

]]>
https://www.p-programowanie.pl/kurs-angular/filtry/feed/ 0
Czy warto iść na studia magisterskie? https://www.p-programowanie.pl/studia-praca/czy-warto-isc-studia-magisterskie/ https://www.p-programowanie.pl/studia-praca/czy-warto-isc-studia-magisterskie/#comments Wed, 22 Mar 2017 19:52:07 +0000 http://www.p-programowanie.pl/?p=3026 Duże zainteresowanie tematem studiów zainspirowało mnie do napisania kolejnego artykułu poświęconego edukacji. Problem, z którym ostatnio się spotkałem był koniecznością podjęcia decyzji: kontynuować studia czy nie? Jestem studentem informatyki dlatego cały artykuł będzie dotyczył właśnie tego kierunku. Kierunek kierunkowi nie równy, dlatego nie wszystkie moje spostrzeżenia mogą mieć odzwierciedlenie gdzie indziej.

Studia dwustopniowe

W mojej skromnej opinii całe zamieszanie ze studiami wynika z wprowadzenia dwustopniowego systemu edukacji wyższej. Każdy absolwent pierwszego stopnia (czy to licencjat, czy to inżynier) musi podjąć decyzję o kontynuowaniu nauki bądź nie. Z jednej strony ogromną pokusą jest oddanie się pracy, bez martwienia się o kolejne kolokwia, zaliczenia, egzaminy. Z drugiej strony rozsądek podpowiada dokończyć sprawę i kilkanaście lat edukacji przypieczętować tytułem magistra.

Nie wiem dlaczego wprowadzono system dwustopniowy na wzór amerykański. Dla mnie osobiście lepiej byłoby gdyby studia były jednolite. Nie stałbym wtedy przed żadnym dylematem. Wielu moich kolegów z którymi dyskutowałem ma podobne wnioski. Na pewno wielu osobom dwustopniowy podział odpowiada. Może okazać się ratunkiem w wielu trudnych życiowych sytuacjach (ciąża, choroba, sprawy osobiste). O wiele łatwiej jest przypieczętować inżyniera/magistra po 3 latach, niż ciągnąć jednolite studia magisterskie trwające 5 lat. Przez 5 lat w życiu może się wiele wydarzyć, więc rozdzielenie tego okresu na dwa mniejsze daje jakąś gwarancję.

Dla mnie niesamowitą wadą obecnego systemu jest zmarnowanie ostatniego semestru na poszczególnych stopniach. Nie jest tajemnicą, że ostatni semestr jest prostszy. Jest w nim mniej zajęć, większą wagę przykłada się do pisania pracy dyplomowej. W systemie dwustopniowym takie semestry są dwa, wiec jest to w sumie rok studiów stracony. Idąc dalej tym tokiem rozumowania, studia magisterskie to rok porządnej nauki. Z trzech semestrów robią się już tylko dwa.

Studiowanie pokrewnego kierunku

Za zaletę dwustopniowego podziału na pewno nie można uznać możliwości studiowania dwóch pokrewnych kierunków studiów. Chociaż brzmi to fajnie, to w życiu rzeczywistym nie ma odwzorowania. Poznałem kilka osób, które wybrały na studia magisterskie inny kierunek studiów niż na pierwszym stopniu. Każda z tych osób na studiach wypadała, powiedzmy, dość słabo. Ktoś kto nie studiował informatyki i ma z nią mało wspólnego, nie ma żadnych szans nadrobić wiadomości z pierwszego stopnia.

Pewnego dnia wdałem się w dyskusję na ten temat z moim profesorem od programowania i też w 100% się ze mną zgodził, wręcz więcej, powiedział że jest to bardzo zły system. Z tym prowadzącym miałem na pierwszym stopniu przedmiot o wzorcach projektowych oraz kolejny przedmiot programowanie obiektowe. Na magisterce z tym samym prowadzącym mieliśmy programowanie obiektowe zaawansowane. Prowadzący zapytał mnie retorycznie: „Jak mam zrealizować z wami zaawansowane programowanie obiektowe, skoro na drugi stopień przyszły osoby bez znajomości podstaw informatyki?”. Taka sytuacja jest niekorzystna zarówno dla prowadzącego, jak i dla studentów. My oczekiwaliśmy szerszej wiedzy, inni oczekiwali wstępu do programowania a prowadzący starał się realizować zajęcia dwóch prędkości.

Magisterka powinna rozszerzyć wiedzę z pierwszego stopnia

Osobiście nigdy nie odważyłbym się na podjęcie studiów drugiego stopnia na kierunku innym niż mój bazowy czyli informatyka. Ukończyłem technikum informatyczne, zdobyłem tytuł inżyniera z zakresu informatyki, skąd nagle miałbym pojawić się na mechanice, matematyce, automatyce lub budownictwie? Zapewne zginąłbym na każdym z tych kierunków ze strzępkami wiedzy jakie posiadam. Istnieje powiedzenie, że jak coś jest do wszystkiego to jest do niczego. Osobiście także uważam, że lepiej być specem w danej, konkretnej dziedzinie (single responsibility – wiadomo podstawa OOP).

Jeżeli ktoś obudził się i stwierdził, że kierunek z pierwszego stopnia nie jest jego powołaniem (wkraczamy tu już na rejony lekko patologiczne), to o wiele lepszym wyborem, byłoby zaczęcie drugiej inżynierki/licencjatu na nowym kierunku, niż kontynuacja magisterki na nowym kierunku.

Dlatego dochodząc do konsensusu, mimo niezaprzeczalnych plusów studiów dwustopniowych dla mnie jest to tandeta. Nikt nie każe mi zmieniać kierunku? – tak zgadzam się. Jednak przez to, że istnieje taka możliwość, poziom studiów magisterskich w moim odczuciu jest zbyt niski. Na drugim stopniu oczekiwałbym rozszerzenia wiadomości z pierwszego stopnia a nie ich powtarzanie i wałkowanie w kółko. Jeżeli ktoś chce podstaw, powinien zacząć kolejne studia pierwszego stopnia (jeśli ma na to czas).

Studia magisterskie zaocznie? Czemu nie

Mimo, że jestem fanem studiów dziennych, jako jedynego słusznego trybu studiowania, to uważam, że studia magisterskie można ukończyć zaocznie. Mam świadomość, że w innym moim artykule prezentuję odmienne podejście. Teraz gdy ujrzałem na oczy problemy opisane w powyższym akapicie stwierdzam, że na magisterkę naprawdę szkoda czasu. Studia magisterskie przesycone są bezużytecznością, niezależnie czy dziennie czy zaocznie. Z tego też powodu bardzo żałuję, że nie było mi pisane studiować na studiach jednolitych. Przypuszczam, że program studiów jednolitych jest po prostu lepiej ułożony, nie sądzę aby w siatce znajdowały się powtórzone przedmioty.

Należy pamiętać, że studia magisterskie wiążą się z koniecznością dłuższego studiowania (4 semestry zamiast 3) oraz koniecznością opłacania czesnego za każdy semestr nauki (co w przypadku informatyków nie powinno być problemem). Plusy i minusy trzeba przekalkulować samemu.

Osobiście ukończyłem studia magisterskie studiując dziennie i pracując na pełny etat, więc mój dzień zaczynał się o 7 rano a kończył o 19 (doliczając siłownię o 22). Nie wiem czy zdecydowałbym się na taki krok drugi raz w życiu.

Dlaczego zdecydować się na magisterkę

Sadzę, że powodów dlaczego warto ukończyć studia magisterskie jest wiele. Jeżeli ukończyłeś studia pierwszego stopnia, oznacza to, że faktyczny proces Twojej edukacji trwa już dobre 15 lat. Naprawdę warto poświęcić ten dodatkowy rok i przypieczętować wszystko magistrem. Dlaczego?

Stopień licencjata lub inżyniera jest pełnoprawnym wykształceniem wyższym w Polsce. We wszystkich małych firmach tyle wystarczy, raczej nikt nigdy nie będzie od Ciebie wymagał magistra. Ważne, abyś znał się na tym co robisz. Sytuacja może wyglądać inaczej w korporacji, gdzie procedury są takie a nie inne. Często w dużych firmach nikogo nie interesują Twoje umiejętności dopóki nie spełnisz określonych kryteriów. Co z tego, że jesteś dobry, skoro zostaniesz odrzucony już na etapie preselekcji przez rekruterów?

Wykształcenie wyższe w postaci tytułu magistra to międzynarodowy tytuł master of science respektowany w bardzo wielu krajach. Natomiast polskie stopnie inżyniera i licencjata respektowane są bardzo różnie. Inaczej patrzą na nie na zachodzie Europy a inaczej w Anglii. Poza tym, zagraniczne firmy przyjmują znaczną uwagę do posiadania tytułu magistra i są mi znane przypadki powrotu z emigracji w celu uzupełnienia wykształcenia (właśnie w zawodzie programisty).

Nikt nie jest w stanie przewidzieć jak potoczy się jego kariera. W zawodzie programisty bardzo częstym zjawiskiem jest zjawisko zawodowego wypalenia się. Dotyka to naprawdę wielu programistów. Jednym ze sposobów przeciwdziałania jest zwyczajnie zmiana stanowiska. Można zostać kierownikiem projektu, architektem systemowym, zająć się menedżerką zamiast klepania kodu. W tym wypadku magister z pewnością okaże się niezbędny. Jest to niejako powiązane z awansem.

Jest to moje spojrzenie na papierek poświadczający posiadanie magistra i właśnie dlatego zdecydowałem się na studia drugiego stopnia.

]]>
https://www.p-programowanie.pl/studia-praca/czy-warto-isc-studia-magisterskie/feed/ 8
Wstrzykiwanie zależności https://www.p-programowanie.pl/kurs-angular/wstrzykiwanie-zaleznosci/ https://www.p-programowanie.pl/kurs-angular/wstrzykiwanie-zaleznosci/#respond Sun, 18 Dec 2016 01:17:00 +0000 http://www.p-programowanie.pl/?p=2990 Rozumienie mechanizmu wstrzykiwania zależności nie jest Ci niezbędne do stworzenia prostej aplikacji AngularJS, jednak na pewno da Ci dobre spojrzenie na działanie całego frameworka. Wstrzykiwanie zależności jest bardzo popularnym wzorcem projektowym, który sprawdza się w aplikacjach modularnych bardzo dobrze. Pisząc aplikację w AngularJS przede wszystkim dzielimy ją na dziesiątki modułów, kontrolerów oraz serwisów dlatego twórcy Angulara nie mogli nie zaimplementować tego mechanizmu.

Idea wstrzykiwania zależności

Rozważmy hipotetyczną sytuację, w której nasz obiekt wywołuję operację wypisywania logów do konsoli. W tym celu w dowolnej funkcji w naszym $scope wywołujemy metodę Console.writeline(). Wypuszczając aplikację produkcyjne logów musimy się pozbyć. Jeżeli nasza aplikacja ma kilka tysięcy linii kodu problematyczne będzie usuwanie wszystkich wywołań tej funkcji w kodzie.

Tutaj z pomocą przychodzi wstrzykiwanie zależności a więc tzw. odwrócenie kontroli. Główna idea wstrzykiwania zależności polega na tym, aby nie wiązać obiektu referencją do innego obiektu z którego korzysta na stałe. Zamiast tego lepiej przekazać tę referencję np. przez konstruktor albo parametr. Dzięki temu zmieniając obiekt wypisywania logów na ekran w jednym miejscu, zmienia się zachowanie całej aplikacji, która korzysta z tego obiektu. Mało tego, możemy wstrzykiwany obiekt podmienić i wstrzyknąć tam coś zupełnie innego.

Wstrzykiwanie zależności do kontrolera

Aby wstrzyknąć zależność do kontrolera w AngularJS wystarczy dopisać nazwę obiektu do parametrów funkcji kontrolera. Właśnie w ten sposób we wszystkich poprzednich wpisach wstrzykiwany był obiekt $scope.:

W kolejnych artykułach do kontrolera trzeba będzie wstrzykiwać zależności do serwisów, aby zwiększyć jego funkcjonalność. W bardziej rozbudowanych aplikacjach często na liście zależności jest ich więcej niż 10.

Adnotowanie wstrzykiwanych obiektów

Wypuszczając aplikację AngularJS produkcyjnie przeważnie minifikuje się jej kod. Po pierwsze aby zmniejszyć rozmiar pliku a po drugie aby kod zaciemnić. Podczas minifikacji nazwy parametrów wszelkich funkcji są zamieniane na jednoliterowe odpowiedniki (można ponieważ JavaScript nie jest silnie typowany). Z tego powodu twórcy AngularJS umożliwili opisywanie wstrzykiwanych obiektów.

Aby adnotować wstrzykiwanie zależności trzeba wypełnić tablicę $inject dostępną na obiekcie funkcji kontrolera. Wygląda to następująco:

var app = angular.module("mainModule", []);

app.controller('simpleCtrl', simpleController);

simpleController.$inject = ['$scope'];
function simpleController($scope) {
	$scope.uzytkownik = {
		login: '',
		haslo: ''
	};
	$scope.logowanie = function() {
		console.log($scope.uzytkownik.login 
					+ ' ' 
					+ $scope.uzytkownik.haslo);
	}
}

Ponieważ wartości tekstowe nie są minifikowane ani zaciemniane (rozwaliłoby to aplikację gdyby było inaczej) adnotacje podawane są jako ciągi znaków. Dzięki temu zamiast korzystać z nazwy $scope w parametrze kontrolera możemy zmienić jej nazwę na dowolnie inną:

var app = angular.module("mainModule", []);

app.controller('simpleCtrl', simpleController);

simpleController.$inject = ['$scope'];
function simpleController(lala) {
	lala.uzytkownik = {
		login: '',
		haslo: ''
	};
	lala.logowanie = function() {
		console.log(lala.uzytkownik.login 
					+ ' ' 
					+ lala.uzytkownik.haslo);
	}
}

Działanie aplikacji zupełnie się nie zmieniło.

Dodawanie adnotacji do wstrzykiwanych zależności jest standardem. Od teraz będzie się pojawiać w każdym kolejnym wpisie o Angularze. Aby kompilator był wstanie dopasować adnotację do zależności pozycje w tablicy adnotacji $inject muszą się pokrywać z kolejnością parametrów funkcji kontrolera. Jest to bardzo ważna zasada.

Notacja inline

W wielu miejscach w internecie można też spotkać się z innym formatem dodawania adnotacji do wstrzykiwanych zależności umieszczonych „w jednej linii”. Aby osiągnąć taki cel, kontrolery muszą być definiowane za pomocą funkcji anonimowej. Wygląda to tak:

// kontroler jako funkcja anonimowa
app.controller('simpleCtrl', function($scope) {
	$scope.uzytkownik = {
		login: '',
		haslo: ''
	};
});

// kontroler jako funkcja anonimowa z adnotacjami
app.controller('simpleCtrl', ['$scope', function($scope) {
	$scope.uzytkownik = {
		login: '',
		haslo: ''
	};
}]);

Być może na pierwszy rzut oka nie jest to dla Ciebie zbyt przejrzysty zapis. Osobiście tez go nie używam. Funkcja anonimowa została poprzedzona nawiasami kwadratowymi i jest ostatnim elementem tablicy adnotacji.

Korzyści płynące ze wstrzykiwania zależności

Mechanizm wstrzykiwania zależności jest typowym wzorcem odwrócenia kontroli (z ang. inversion of control). Przydaje się przede wszystkim podczas pisania testów do kodu. Używając serwisów, których głównym zadaniem jest przejmowanie zaawansowanej logiki aby nie zapychać kontrolerów, możemy wstrzykiwać ich różne instancje bez jakichkolwiek zmian kodu kontrolera.

Przykładowo, dla testów możemy zmienić zależność bazy danych i wstrzyknąć instancję obiektu, który zwraca statyczne dane JSON. Podobną strategię wstrzykiwania zależności realizuje np. framework Ninject na platformie ASP.NET MVC. Korzyści i zasady postępowania są takie same.

Bez stosowania wstrzykiwania zależności kod programu jest kodem nietestowalnym. Nie da się napisać testów dla kontrolera, który pobiera dane z zewnętrznego źródła. Jedyną możliwością jest odpięcie źródła danych (tzn. bazy danych) i zastąpienie ich danymi testowymi (fikcyjnymi).

]]>
https://www.p-programowanie.pl/kurs-angular/wstrzykiwanie-zaleznosci/feed/ 0
Zdarzenia https://www.p-programowanie.pl/kurs-angular/zdarzenia/ https://www.p-programowanie.pl/kurs-angular/zdarzenia/#respond Sun, 18 Dec 2016 00:16:52 +0000 http://www.p-programowanie.pl/?p=2977 Zdarzenia są bardzo ważnym mechanizmem umożliwiającym interakcję użytkownika z naszą aplikację. Oczywiście, w AngularJS zdarzenia zostały obsłużone wyjątkowo dobrze. Czym są zdarzenia? Są to przechwycenia wszelkich operacji jakie może wykonać użytkownik w naszej aplikacji np. kliknięcie myszką, naciśnięcie klawisza.

Podpinanie zdarzeń

Zdarzenia w AngularJS podpinamy poprzez dodanie do dowolnego elementu HTML odpowiedniej dyrektywy reprezentującej zdarzenie. Jako parametr dyrektywy należy podać funkcję zwrotną, która zostanie wykonana w danej sytuacji. Składania zdarzenia:

<element nazwa_zdarzenia="funkcja" />

A więc przykładowo zdarzenie kliknięcia w przycisk wywołujące funkcje $scope.logowanie() wygląda następująco:

<button ng-click="logowanie()">Zaloguj</button>

Pełny kod działającej aplikacji:

<script>
	var app = angular.module("mainModule", []);
 
	app.controller('simpleCtrl', simpleController);
	
	function simpleController($scope) {
		$scope.uzytkownik = {
			login: '',
			haslo: ''
		};
		$scope.logowanie = function() {
			console.log('wpisany login: ' + $scope.uzytkownik.login);
			console.log('wpisane haslo: ' + $scope.uzytkownik.haslo);
			console.log('ktos kliknął logowanie!');
		};
	}
</script>
 
<body ng-app="mainModule" ng-controller="simpleCtrl">
	<p>Login: <input ng-model="uzytkownik.login" /></p>
	<p>Hasło: <input ng-model="uzytkownik.haslo" /></p>
	<button ng-click="logowanie()">Zaloguj</button>
</body>

Do przycisku logowania zostało podpięte zdarzenie ng-click wywołujące funkcje logowanie(). Bindując zmienne lub funkcje podpięte do $scope z warstwą widoku zawsze pomijamy nazwę $scope. Efekt działania jest następujący:

W przypadku podania nieistniejącej nazwy funkcji do parametru dyrektywy zdarzenia, niestety nic się nie stanie. W konsoli nie pojawi się żaden błąd. Jeżeli coś nie działa a w konsoli nie ma błędów, warto w pierwszej kolejności sprawdzić czy zdarzenia podpięte są pod istniejące funkcje lub czy nie zapomnieliśmy o nawiasach.

Lista dostępnych zdarzeń

Oto lista dostępnych zdarzeń:

Zdarzenie Wywoływany gdy
ng-blur Dany element traci aktywność (traci focus)
ng-change Zmieni się model ng-model danego elementu
ng-click Kliknięcie myszką
ng-copy Nastąpi skopiowanie wartości elementu
ng-cut Nastąpi wycięcie wartości elementu
ng-dblclick Podwójne kliknięcie myszką
ng-focus Dany element zostaje aktywny (focus)
ng-keydown Naciśnięcie klawisza
ng-keypress Naciśnięcie klawisza (rozróżnia wielkość liter)
ng-keyup Puszczenie klawisza
ng-mousedown Naciśnięcie klawisza myszy
ng-mouseenter Najechanie myszą na element gdziekolwiek
ng-mouseleave Zjechanie myszą z elementu
ng-mousemove Poruszanie myszą po elemencie
ng-mouseover Najechanie myszą na element lub jego dzieci
ng-mouseup Puszczenie klawisza myszy
ng-paste Wklejenie wartości do elementu

Większości wymienionych zdarzeń w codziennej pracy nie używa się. Najważniejsze zdarzenia, które na pewno należy zapamiętać to ng-click oraz ng-change. Za pomocą kliknięcia możemy obsłużyć wszystkie przyciski i formularze w aplikacji.

Z kolei ng-change to wywołuje funkcje zwrotną podczas jakiejkolwiek zmiany modelu przez użytkownika, więc może np. przeliczyć w tle dane. Jest to dobra alternatywa dla zakładania obserwatorów na $scope o czym będzie w innym artykule.

Zdarzenie ng-change

Wcześniej czy później zdasz sobie sprawę, że ng-change jest jednym z najbardziej przydatnych zdarzeń. Aby zdarzenie działało, musi być nałożone na obiekt posiadający model podpięty dyrektywą ng-model. To właśnie zmiana wartości modelu wpływa na wywołanie ng-change. Przykładowe użycie:

<script>
	var app = angular.module("mainModule", []);
 
	app.controller('simpleCtrl', simpleController);
	
	function simpleController($scope) {
	
		$scope.check = false;
		$scope.ilosc = 0;
		
		$scope.klikniecie = function() {
			$scope.ilosc++;
		}
	}
</script>
 
<body ng-app="mainModule" ng-controller="simpleCtrl">
	<p>Zaznacz checkbox: <input type="checkbox" ng-model="check" ng-change="klikniecie()" /></p>
	<p>
		Checkbox jest 
		<span ng-if="check" style="color:#00CE00; font-weight:bold;">zaznaczony</span>
		<span ng-if="!check" style="color:#FF0000; font-weight:bold;">odznaczony</span>
	<p>
	<p>Został kliknięty <b>{{ilosc}}</b> razy</p>
</body>

Efekt działania jest przewidywalny:

Przy jakiejkolwiek zmianie modelu podpiętego do pola typu checkbox zostaje wywołana funkcja klikniecie(). Zwiększa ona wartość zmiennej ilosc, który jest następnie wyświetlany za pomocą wyrażenia {{ilosc}}. Dodatkowo zastosowana dyrektywa ng-if sprawdza aktalny stan pola i wyświetla odpowiedni span z tekstem.

]]>
https://www.p-programowanie.pl/kurs-angular/zdarzenia/feed/ 0
Dyrektywa ngIf https://www.p-programowanie.pl/kurs-angular/dyrektywa-ngif/ https://www.p-programowanie.pl/kurs-angular/dyrektywa-ngif/#respond Sat, 17 Dec 2016 20:24:35 +0000 http://www.p-programowanie.pl/?p=2956 Programowanie we wszystkich językach opiera się na budowaniu przepływu programu za pomocą instrukcji warunkowych. W AngularJS po stronie kontrolera używamy dokładnie takich samych konstrukcji jak w języku JavaScript. Oznacza to, że bez żadnych problemów możemy korzystać z ifswitch, operatorów trójargumentowych i wszystkiego co wymyślimy. Operatory warunkowe dostępne w kontrolerach nie są dostępne w warstwie widoku (a więc pliku HTML). Zamiast tego Twórcy AngularJS przygotowali dla programistów specjalną dyrektywę ng-if.

Dyrektywa ngIf

Aby użyć dyrektywy ng-if należy osadzić ją w dowolnym atrybucie HTML. Jej wartością może być dowolne wyrażenie. Przykładowo następujące porównanie nigdy nie zostanie spełnione:

<script>
	var app = angular.module("mainModule", []);

	app.controller('simpleCtrl', simpleController);
	
	function simpleController($scope) {
		$scope.imie = "";
	}
</script>

<body ng-app="mainModule" ng-controller="simpleCtrl">
	<div ng-if="1 == 5">
		Niewidoczny element
	</div>
</body>

Podglądając wygenerowany kod można zauważyć, że jeżeli warunek dyrektywy nie jest spełniony to nie zostanie wygenerowane statyczne drzewo DOM dla elementu:

Jest to bardzo ważny wniosek. Używając dyrektywy ng-if należy pamiętać, że za każdym razem przetworzone zostanie statyczne drzewo DOM dokumentu. W przypadku kiedy warunek nie jest spełniony, zostaje wycięta cała treść strony zagnieżdżona w dyrektywie.

Tak samo jak w przypadku dyrektywy ng-repeat elementy zagnieżdżone mają podpięty swój osobny $scope.

Przykład użycia dyrektywy ng-if może wyglądać następująco:

<script>
	var app = angular.module("mainModule", []);
 
	app.controller('simpleCtrl', simpleController);
	
	function simpleController($scope) {
		$scope.osoba = {
			imie: "",
			nazwisko: ""
		};
	}
</script>
 
<body ng-app="mainModule" ng-controller="simpleCtrl">
	<p>Imię: <input ng-model="osoba.imie" /></p>
	<p>Nazwisko: <input ng-model="osoba.nazwisko" /></p>
	
	<p><label>Czy pracujesz? <input type="checkbox" ng-model="osoba.czyPracuje" /></label></p>
	<p ng-if="osoba.czyPracuje">
		Podaj nazwę firmy: <input ng-model="osoba.firma" />
	</p>
	
	<br>
	<p>Nazywasz się <b>{{osoba.imie}} {{osoba.nazwisko}}</b>.<p>
	<p ng-if="osoba.czyPracuje && osoba.firma.length">Pracujesz w <b>{{osoba.firma}}</b>.<p>
</body>

Wnioski na temat ng-if są następujące:

  • jeżeli wartość wyrażenia dyrektywy nie jest spełniona, zmodyfikowane zostaje statyczne drzewo DOM dokumentu.
  • dla elementów znajdujących się w dyrektywie zostaje utworzony osobny $scope (zagnieżdżony względem $scope kontrolera)

Nadużywanie ng-if może znacznie spowolnić ładowanie strony. Uzależniając wyświetlanie danych elementów od warunków okazuje się, że nasze $scopy stają się bardzo zagnieżdżone i nieuporządkowane. Z tego powodu powstała dyrektywa ng-show oraz ng-hide.

Dyrektywy ngShow i ngHide

Twórcy AngularJS zauważyli, że nie za każdym razem kiedy zależy nam na ukryciu danego elementu chcemy od razu przebudowywać drzewo DOM co wiąże się ze spadkami wydajności.

Dyrektywy ng-show oraz ng-hide działają w ten sam sposób, tzn. przyjmują jako parametr wyrażenie, jednak ukrywanie elementów odbywa się poprzez dodanie do nich atrybutu CSS o nazwie display: none !important. W większości przypadków lepszym wyborem będzie użycie właśnie wyżej wymienionych dyrektyw. Bardzo dobrze sprawdzają się do budowania elementów typu pokaż/ukryj (z ang. toggle). Tylko nieznacznie obciążają pamięć.

Przykładowy kod:

<script>
	var app = angular.module("mainModule", []);
 
	app.controller('simpleCtrl', simpleController);
	
	function simpleController($scope) {
		$scope.toggle = false;
	}
</script>
 
<body ng-app="mainModule" ng-controller="simpleCtrl">
	<h4>Lista zakupów <a href="#" ng-click="toggle = !toggle">[pokaż/ukryj]</a></h4>
	<ul ng-show="toggle">
		<li>3 bułki</li>
		<li>majonez</li>
		<li>czekolada</li>
	</ul>
</body>

W przykładzie użyte zostało zdarzenie ng-click. Nie musisz się nim przejmować, jego jedynym zadaniem jest nadawanie wartości zmiennej toggle na przeciwną względem aktualnej. Zamiast podpięcia ng-click możliwe było skorzystanie np. z pola typu checkbox z podpiętym modelem. Zdarzenia zostaną opisane w kolejnym artykule. Działanie prezentuje się następująco:

Na załączonym gifie widać wyraźnie, że do ukrycia elementu zostaje użyta klasa ng-hide. Drzewo DOM dokumentu pozostaje bez zmian, nie zostają też utworzone nowe $scope. Dyrektywa ng-hide jest przeciwieństwem ng-show i można używać ich zamiennie. Obydwoma uzyskujemy ten sam efekt, ewentualną różnicą jest dodanie negacji przed wyrażeniem.

Kiedy używać ngShow a kiedy ngIf

Dyrektywy ng-show używamy w większości wypadków, szczególnie tam gdzie zależy nam aby na chwilę ukryć pewne fragmentu strony w zależności od stanu aplikacji.

Ng-if przydaje się gdy szacujemy, że fragment drzewa HTML który ukrywa nie zostanie pokazany w dużej ilości przypadków. Nie ma sensu generować 100 linijek kodu trzymanych w pamięci przeglądarki i inicjalizujących swoje modele jeżeli dana funkcjonalność używana jest przez 20% odwiedzających użytkowników. Dodatkowo, ng-if przydaje się podczas walidacji formularzy, co zostanie opisane w innym artykule. Pokrótce chodzi o to, że ukrywając input który jest wymagany, nie zostaje on zaliczony do elementów walidowanego formularza. Ng-show nie daje takich efektów.

]]>
https://www.p-programowanie.pl/kurs-angular/dyrektywa-ngif/feed/ 0
Dyrektywa ngRepeat https://www.p-programowanie.pl/kurs-angular/dyrektywa-ngrepeat/ https://www.p-programowanie.pl/kurs-angular/dyrektywa-ngrepeat/#respond Fri, 16 Dec 2016 22:51:02 +0000 http://www.p-programowanie.pl/?p=2942 W poprzednich artykułach poznaliśmy kilka podstawowych dyrektyw wbudowanych we framework AngularJS. Pierwsza z nich ng-app służy do inicjalizacji Agulara. Kolejna ng-controller służy do podłączenia kontrolera w danym miejscu statycznego drzewa DOM. Kontroler można podłączyć też automatycznie za pomocą konfiguracji routingu jednak o tym innym razem.  Ostatnią poznaną dyrektywą jest ng-model niezbędna aby uruchomić mechanizm dwustronnego bindowania pomiędzy widokiem a kontrolerem.

Dyrektywa ngRepeat

Bardzo użyteczną dyrektywą wbudowaną w AngularJS jest ng-repeat. Dzięki niej możemy iterować wartości dowolnej kolekcji i wyświetlać je po stronie widoku. Oczywiście w Angularze czy też JavaScripcie możemy używać pętli, jednak nie działają one po stronie drzewa DOM dokumentu. Dyrektywa ngRepeat natomiast tak. Podstawowy schemat użycia ng-repeat jest następujący:

ng-repeat="<element> in <kolekcja>"

Na potrzeby testów podepnijmy do $scope kontrolera listę liczb. Możemy ją bardzo prosto wyświetlić w widoku następująco:

<script>
	var app = angular.module("mainModule", []);

	app.controller('simpleCtrl', simpleController);
	
	function simpleController($scope) {
		$scope.listaLiczb = [1, 2, 3, 4, 5, 6];
	}
</script>

<body ng-app="mainModule" ng-controller="simpleCtrl">
	<span ng-repeat="liczba in listaLiczb">{{liczba}}, </span>
</body>

Wynikiem działania powyższego kodu jest lista liczb oddzielonych przecinkami. Ng-repeat jest dyrektywą, która musi zostać osadzona w jakimś elemencie HTML. Następnie klonuje ten element tyle razy jaki jest rozmiar przeglądanej kolekcji. Aby wyświetlić aktualnie iterowany element posługujemy się klamrowym operatorem wyrażenia. Oto podgląd kodu strony w narzędziach deweloperskich Chrome:

Oprócz powtórzenia span kilka razy, został on obłożony dodatkowymi komentarzami, które są niezbędne do debugowania platformy AngularJS i nie powinny nas aktualnie interesować. Ciekawym jest fakt, że powyższy kod zostaje dynamicznie wygenerowany dopiero po fazie ładowania i kompilowania Angulara (czyli po załadowaniu głównego modułu, kontrolera i przetworzeniu dyrektyw drzewa DOM). Sprawdzając źródło strony widzimy tyle:

Naszym oczom ukazuje się fragment kodu nieskompilowany. Niestety tak samo widzą go roboty sieciowe, dlatego właśnie AngularJS nie naddaje się do pisania stron, które mają być pozycjonowane. Wszelkie parsery, pająki i roboty nie są wstanie przetworzyć Angulara  i nie widzą treści. (update 2017.03.01 wyjątkiem na dzień dzisiejszy jest Google, który parsuje AngularJS).

Iterowanie kolekcji obiektów

Oczywiście dyrektywa ng-repeat jest znacznie potężniejsza niż wynika z poprzedniego akapitu. Można nią bez problemowo iterować i wyświetlać kolekcje obiektów. Stwórzmy przykładową kolekcję osób i osadźmy ją w tabelce:

<script>
	var app = angular.module("mainModule", []);

	app.controller('simpleCtrl', simpleController);
	
	function simpleController($scope) {
		$scope.osoby = [
			{imie: "Karol", nazwisko: "Trybulec", wiek: 24},
			{imie: "Arek", nazwisko: "Kowalski", wiek: 12},
			{imie: "Jarek", nazwisko: "Nowacki", wiek: 51},
			{imie: "Marek", nazwisko: "Zawadzki", wiek: 21},
		];
	}
</script>

<body ng-app="mainModule" ng-controller="simpleCtrl">
	<table border="1" cellpadding="10">
		<tr ng-repeat="osoba in osoby">
			<td>{{osoba.imie}}</td>
			<td>{{osoba.nazwisko}}</td>
			<td>{{osoba.wiek}}</td>
		</tr>
	</table>
</body>

Wyliczenie działa bezproblemowo. Wyświetlone zostały wszystkie elementy i wrzucone do tabelki:

Iterowanie atrybutów obiektu

Dyrektywa ng-repeat została wyposażona w możliwość iterowania atrybutów danego obiektu. W tym celu należy posłużyć się składnią:

ng-repeat="(<klucz>, <wartosc>) in <obiekt>"

Przykładowy kod wygląda następująco:

<script>
	var app = angular.module("mainModule", []);
 
	app.controller('simpleCtrl', simpleController);
	
	function simpleController($scope) {
		$scope.osoba = {
			imie: "Karol",
			nazwisko: "Trybulec",
			kraj: "Polska",
			plec: "mężczyzna"
		};
	}
</script>
 
<body ng-app="mainModule" ng-controller="simpleCtrl">
	<table border="1" cellpadding="10">
		<tr ng-repeat="(klucz, wartosc) in osoba">
			<td>{{klucz}}</td>
			<td>{{wartosc}}</td>
		</tr>
	</table>
</body>

Iterowanie zagnieżdżonych kolekcji

Dyrektywę ng-repeat można zagnieżdżać wewnątrz siebie, jeżeli istnieje taka potrzeba. Taka sytuacja może wystąpić np. w przypadku posiadania kolekcji kolekcji, lub listy obiektów które posiadają kolekcję jako atrybut. Przykładowy kod wygląda następująco:

<script>
	var app = angular.module("mainModule", []);
 
	app.controller('simpleCtrl', simpleController);
	
	function simpleController($scope) {
		$scope.uczniowie = [
			{
				imie: "Karol",
				przedmioty: [
					{
						nazwa: "matematyka", 
						oceny: [5,4,3]
					},
					{
						nazwa: "fizyka", 
						oceny: [3,2,4,1]
					}
				]
			},
			{
				imie: "Arek",
				przedmioty: [
					{
						nazwa: "j. polski", 
						oceny: [5,1,2,4]
					}
				]
			}
		];
	}
</script>
 
<body ng-app="mainModule" ng-controller="simpleCtrl">
	<table border="1" cellpadding="10">
		<tr ng-repeat="osoba in uczniowie">
			<td>{{osoba.imie}}</td>
			<td>
				<div ng-repeat="przedmiot in osoba.przedmioty">
					przedmiot: {{przedmiot.nazwa}} <br>
					oceny: <span ng-repeat="ocena in przedmiot.oceny">{{ocena}}</span>
					
					<br><br>
				</div>
			</td>
		</tr>
	</table>
</body>

Efekt działania programu jest następujący:

Przy tworzeniu takich konstrukcji trzeba kierować się zdrowym rozsądkiem. W niektórych przypadkach optymalniejsze może okazać się przetworzenie kolekcji w jezyku JavaScript i uproszenie jej, a następnie przesłanie jej do dyrektywy ng-repeat. Operacje na drzewie DOM dokumentu są kosztowne i w przypadku skomplikowanych szablonów mogą spowalniać działanie przeglądarki.

$scope elementów kolekcji

Każda iteracja ng-repeat i wyświetlenie elementu tworzy dodatkowy osobny $scope, który jest dzieckiem $scope aktualnego kontrolera. Dlaczego tak jest wynika wprost z budowy Angularowych dyrektyw i zostanie wytłumaczone w dalszych częściach kursu. Podgląd w ngInspector wygląda następująco:

Każdy $scope pojedynczego elementu kolekcji dostaje swoje unikalne atrybuty, które mogą się nam bardzo przydać. Oto ich lista:

  • $index (number) – indeks aktualnego elementu
  • $first (boolean) – flaga informująca czy jest to pierwszy element (pierwsza iteracja na kolekcji)
  • $middle (boolean) – flaga informująca czy jesteśmy równo w środku kolekcji
  • $last (boolean) – flaga informująca czy jest to ostatni element (ostatnia iteracja na kolekcji)
  • $even (boolean) – flaga informaująca czy jest to element parzysty
  • $odd (boolean) – flaga informująca czy jest to element nieparzysty

Z wymienionych atrybutów możemy korzystać w każdym miejscu struktury HTML znajdującej się wewnątrz ng-repeat. Bardzo przydatny jest atrybtut $index oraz $first i $last. Często wykorzystywane są do tworzenia domknięć pojemników, listingów i tabel (np. gdy ostatni element tabeli musi mieć mocniejsze podkreślenie). Aby to osiągnąć trzeba skorzystać z instrukcji warunkowej o czym w będzie w następnej lekcji.

Optymalizacja kolekcji

Kolekcja wyświetlona za pomocą dyrektywy ng-repeat zostaje osadzona w statycznym drzewie DOM dokumentu. Podmiana referencji lub nawet przeładowanie kolekcji spowoduje usunięcie wszystkich jej elementów i wyświetlenie ich ponownie. W przypadku kolekcji większej niż 500 elementów użytkownik odczuje półsekundowe spowolnienie przeglądarki.

Aby móc poruszać się po kolekcji obiektów Angular dodaje każdemu elementowi kolekcji atrybut $$hashKey. Stanowi on unikalny identyfikator dla każdego elementu w aplikacji. Jeżeli zmieniony zostanie element bez zmiany jego $$hashKeya, wtedy w dokumencie odświeżają się jedynie jego atrybuty (bez usunięcia i utworzenia elementu na nowo). Dzięki temu, zmiana atrybutów pojedynczego elementu kolekcji jest szybka i odbywa się w czasie rzeczywistym (zostaje on zlokalizowany po $$hashKey).

Niestety podmiana referencji kolekcji spowoduje zmianę hashy, dlatego mimo że obiekty są takie same (mają takie same atrybuty) struktura DOM dokumentu i tak zostanie przeładowana. Aby nie identyfikować elementów kolekcji po $$hashKey została do ng-repeat wprowadzona opcja track by. Ma ona następującą strukturę:

ng-repeat="<element> in <kolekcja> track by <unikalny_atrybut>"

Przykładowy kod wygląda następująco:

<script>
	var app = angular.module("mainModule", []);
 
	app.controller('simpleCtrl', simpleController);
	
	function simpleController($scope) {
		$scope.przedmioty = [
			{
				id:1, nazwa: "matematyka", 
			},
			{
				id: 2, nazwa: "fizyka", 
			}
		];
	}
</script>
 
<body ng-app="mainModule" ng-controller="simpleCtrl">
	<table border="1" cellpadding="10">
		<tr ng-repeat="przedmiot in przedmioty">
			<td>{{przedmiot.nazwa}}</td>
		</tr>
	</table>
</body>

Gdy podejrzymy $scope aplikacji, zobaczymy że obiekty na liście posiadają unikalny hash doklejony przez Angulara:

 

Ponieważ elementy kolekcji posiadają unikalny atrybut id możemy wykorzystać go zamiast unikalnego hasha. Kod po modyfikacji będzie wyglądał następująco:

<body ng-app="mainModule" ng-controller="simpleCtrl">
	<table border="1" cellpadding="10">
		<tr ng-repeat="przedmiot in przedmioty track by przedmiot.id">
			<td>{{przedmiot.nazwa}}</td>
		</tr>
	</table>
</body>

Podglądając $scope aplikacji przekonamy się, że atrybuty $$hashKey zniknęły. Dzięki temu zabiegowi, przeładowanie kolekcji czy nawet podmiana jej referencji nie spowoduje skasowania wszystkich elementów w strukturze DOM dokumentu. Angular znajdzie elementy, których atrybuty się zmieniły, a następnie zidentyfikuje je po unikalnym identyfikatorze i uaktualni. Korzystanie z opcji track by potrafi znacząco przyśpieszyć działanie aplikacji wykorzystujących kolekcje kilku a nawet kilkunastokrotnie.

Kolekcje typów prostych

Bardzo niebezpieczne jest wyświetlanie kolekcji złożonych z typów prostych. Atrybut $$hashKey może być dodany tylko do obiektu. Iterując kolekcję liczb (np. listę ocen) Angular nie może identyfikować poszczególnych elementów po unikalnym hashu. Z tego powodu, jeżeli kolekcja typów prostych posiada dwie takie same wartości naszym oczom ukarze się błąd:

Error: [ngRepeat:dupes] Duplicates in a repeater are not allowed. Use ‚track by’ expression to specify unique keys. Repeater: ocena in oceny, Duplicate key: number:1, Duplicate value: 1

Przykładowym błędnym kodem jest następująca lista ocen złożona z typów prostych:

<script>
	var app = angular.module("mainModule", []);
 
	app.controller('simpleCtrl', simpleController);
	
	function simpleController($scope) {
		$scope.oceny = [1,2,3,4,5,1];
	}
</script>
 
<body ng-app="mainModule" ng-controller="simpleCtrl">
	<table border="1" cellpadding="10">
		<tr ng-repeat="ocena in oceny">
			<td>ocena</td>
		</tr>
	</table>
</body>

Rozwiązaniem tego problemu jest nie wyświetlanie kolekcji typów prostych za pomocą ng-repeat. Wychodząc na przeciw wymaganiom programistów w jednej z wersji Angulara została wprowadzona możliwość indeksowania kolekcji posługując się ich numerem porządkowym w kolekcji (czyli indeksem kolekcji). Kod wykorzystujący indeksowanie po numerze kolekcji wygląda następująco:

<script>
	var app = angular.module("mainModule", []);
 
	app.controller('simpleCtrl', simpleController);
	
	function simpleController($scope) {
		$scope.oceny = [1,2,3,4,5,1];
	}
</script>
 
<body ng-app="mainModule" ng-controller="simpleCtrl">
	<table border="1" cellpadding="10">
		<tr ng-repeat="ocena in oceny track by $index">
			<td>ocena</td>
		</tr>
	</table>
</body>

Powyższy kod nie spowoduje wyświetlenia błędu. Mimo tego, lepszym rozwiązaniem jest obudowanie kolekcji typów prostych w obiekt i atrybut (do obiektu możliwe jest doklejenie hasha).

]]>
https://www.p-programowanie.pl/kurs-angular/dyrektywa-ngrepeat/feed/ 0
Scope i wyrażenia https://www.p-programowanie.pl/kurs-angular/scope-i-wyrazenia/ https://www.p-programowanie.pl/kurs-angular/scope-i-wyrazenia/#respond Fri, 16 Dec 2016 20:42:37 +0000 http://www.p-programowanie.pl/?p=2913 Zrozumienie tego artykułu jest kluczowe aby móc tworzyć aplikacje w AngularJS. Do tej pory powinieneś wiedzieć czym jest Angular, umieć utworzyć moduł główny aplikacji i podpiąć do niego kontroler. Zdefiniowany przez nas moduł oraz kontroler zostają załadowane do pamięci w fazie ładowania. Następnie w fazie kompilacji AngularJS analizuje statyczne drzewo DOM dokumentu zbudowane ze znaczników HTML. W momencie natrafienia na dyrektywy ng-application oraz ng-controller zostaje utworzony nowy $scope.

Czym jest $scope

W większości języków programowania możemy definiować własne zasięgi zmiennych, w których są one widoczne. W C++ każda funkcja posiada swój własny zasięg ograniczony przez nawiasy klamrowe. Nic nie stoi na przeszkodzie aby w środku funkcji zagnieździć kolejne nawiasy klamrowe definiujące nowy zakres widoczności.

$scope w AngularJS jest niczym innym jak zasięgiem widoczności danego kontrolera i tworzy on warstwę widok-model wzorca MVVM. Każdy kontroler posiada swój unikalny $scope do którego przypięte są funkcje oraz zmienne widoczne w warstwie widoku. $scope jest jedynym elementem, który jest w stanie wystawić dane dla widoku i komunikować się z widokiem.

W czystym JavaScripcie zasięgi zmiennych tworzone były poprzez funkcje anonimowe, w AngularJS posiadamy kontrolery, które także są funkcjami.

$scope zostaje utworzony automatycznie dla każdego kontrolera, który zostanie zainicjalizowany dyrektywą ng-controller (lecz nie tylko o czym później). Oprócz tego, podczas inicjalizacji aplikacji za pomocą dyrektywy ng-app zostaje utworzony tzw. $rootScope, a więc zasięg bazowy dla wszystkich innych.

W przypadku zagnieżdżonych kontrolerów zasięgi także zostają zagnieżdżone. Każdy $scope posiada referencję do dziecka child-scope oraz do rodzica parent-scope. Rodzicem nadrzętnym jest $rootScope. Nie jest to informacja, która może Ci się przydać na początku kursu, jednak warto o takim fakcie wiedzieć. Rozwieje to Twoje wątpliwości szczególnie podczas przeglądania $scope za pomocą wtyczki ngInspector.

Dwustronne bindowanie danych

Potęgą frameworków JavaScriptowych jest niezaprzeczalnie mechanizm dwustronnego bindowania danych two-way data binding. Jest to element obiektu $scope i zapewnia dynamiczne odświeżanie danych pomiędzy widokiem a view-modelem (czy też upraszczając kontrolerem).

Każdy kto pisał cokolwiek w JavaScript lub jego młodszym bracie jQuery wie jak uciążliwe jest wymienianie danych pomiędzy elementami HTML a skryptem. W AngularJS nie trzeba się o to martwić.

Wyrażenia

Zanim przetestujemy mechanizm dwustronnego bindowania danych trzeba dowiedzieć się czym są wyrażenia. Wyrażenia w AngularJS służą do przekazywania danych pomiędzy kontrolerem a widokiem (w jedną stronę!):

Istnieją dwie metody na przekazanie wyrażenia do widoku:

  • Przekazanie wyrażenia za pomocą nawiasów klamrowych np. {{wyrażenie}}
  • Przekazanie wyrażenia za pomocą wbudowanej dyrektywy np. ng-bind="wyrażenie"

Wyrażenia w AngularJS są takie same jak w JavaScript, mogą zawierać zmienne, literały oraz operatory. W celu przetestowania wyrażeń proponuję podpiąć do $scope zmienną o dowolnej treści. Przykładowo utworzę atrybut imie o wartości „Karol”. Definiowanie atrybutów i funkcji obywa się tak samo jak w przypadku JavaScript.:

<script>
	var app = angular.module("mainModule", []);

	app.controller('simpleCtrl', simpleController);
	
	function simpleController($scope) {
		$scope.imie = "Karol"
	}
</script>

<body ng-app="mainModule" ng-controller="simpleCtrl">
	<p>Wynik dodawania 5+2 to {{5+2}}</p>
	<p>Masz na imię {{imie}}<p>
</body>

Po otworzeniu powyższego kodu w przeglądarce Chrome przekonasz się, że zmienne zostały wyświetlone w poprawny sposób. Nawiasy klamrowe można zastąpić dyrektywą ng-bind:

<p>Masz na imię {{imie}}<p>
<p>Masz na imię <span ng-bind="imie"></span><p>

Wynik dwóch powyższych linijek będzie ten sam. Jedyna różnica w przypadku posługiwania się dyrektywą jest to, że musi być ona osadzona w jakimś znaczniku HTML. Z tego powodu konieczne było utworzenie dodatkowego spana wewnątrz akapitu aby można było zbindować do niego wartość zmiennej. Z tego powodu bardziej popularne i wygodniejsze są nawiasy klamrowe.

Spójrzmy jak wygląda struktura zakresów podglądając je za pomocą wtyczki ngInspector:

Wyraźnie widać moduł, podpięty do niego $rootScope oraz jego dziecko czyli $scope naszego kontrolera. Uwaga, aby dodatek ngInspector działał plik jaki badamy musi być serwowany przez serwer np. przez serwer lokalny. Odwołując się do pliku za pomocą ścieżki file:// wtyczka nie uruchomi się. Szybki darmowy serwer HTTP jest wbudowany np. w npm (http-server). Można użyć też XAMPA lub innych podobnych.

Modele

Użycie modelu dostarcza nam 100% prawdziwego mechanizmu dwustronnego bindowania, z którego słyną frameworki JavaScriptowe. W przypadku modelu bindowanie odbywa się w dwie strony, a więc zmiany które zajdą w formularzu są widocznie w kontrolerze oraz na odwrót:

Modele można podpinać wyłącznie do kontrolek HTMLowych czyli np. do elementów input. Jedyną możliwością podpięcia modelu jest użycie wbudowanej dyrektywy ng-model. Przykładowo:

<script>
	var app = angular.module("mainModule", []);

	app.controller('simpleCtrl', simpleController);
	
	function simpleController($scope) {
		$scope.imie = "Karol"
	}
</script>

<body ng-app="mainModule" ng-controller="simpleCtrl">
	<p>Masz na imie <input ng-model="imie" /><p>
</body>

Powyższy kod podpina do pola tekstowego model $scope.imie. Po uruchomieniu kodu wartość zmiennej zostanie automatycznie zbindowana do widoku.

Pełny przykład bindowania danych

Sprawdźmy teraz, dlaczego AngularJS jest tak fajny jak o nim mówią. Poznaliśmy podstawowe mechanizmu umożliwiające bindowania danych, spróbujmy je ze sobą połączyć. W tym celu zdefiniujmy model dla zmiennej imie oraz nazwisko a następnie za pomocą wyrażenia wyświetlmy jak nazywa się użytkownik:

<script>
	var app = angular.module("mainModule", []);

	app.controller('simpleCtrl', simpleController);
	
	function simpleController($scope) {
		$scope.imie = "";
		$scope.nazwisko = "";
	}
</script>

<body ng-app="mainModule" ng-controller="simpleCtrl">
	<p>Imię: <input ng-model="imie" /></p>
	<p>Nazwisko: <input ng-model="nazwisko" /></p>
	<br>
	<p>Nazywasz się {{imie}} {{nazwisko}}.<p>
</body>

W powyższym przykładzie użytkownik może wpisać do dwóch pól tekstowych wartości tekstowe. Jest dla nich zdefiniowany model. Za pomocą wyrażeń wartość zostaje automatycznie wyświetlona niżej jako zwykły element strony HTML:

Jak widać na załączonym przykładzie AngularJS odświeża dane ze $scope w czasie rzeczywistym. Jest to niewątpliwie jego największa zaleta. Używając czystego JavaScriptu ilość utworzonego kodu byłaby ogromna. Nawet używając jQuery pomógłby on jedynie szybciej załapać uchwyt dla poszczególnego pola tekstowego – nic więcej.

Grupowanie w obiekty i czysty scope

Pisząc aplikacje w AngularJS musimy dbać o porządek w kontrolerze a przede wszystkim o porządek w $scope. Nigdy nie należy podpinać zmiennych bezpośrednio do scope szczególnie jeżeli tworzą jakiś model biznesowy. Powyższy przykład pokazany dla celów naukowych jest błędny, o wiele lepszym rozwiązaniem będzie zgrupowanie danych osobowych do obiektu osoba. Lepsza organizacja powyższego przykładu:

<script>
	var app = angular.module("mainModule", []);

	app.controller('simpleCtrl', simpleController);
	
	function simpleController($scope) {
		$scope.osoba = {
			imie: "",
			nazwisko: ""
		};
	}
</script>

<body ng-app="mainModule" ng-controller="simpleCtrl">
	<p>Imię: <input ng-model="osoba.imie" /></p>
	<p>Nazwisko: <input ng-model="osoba.nazwisko" /></p>
	<br>
	<p>Nazywasz się {{osoba.imie}} {{osoba.nazwisko}}.<p>
</body>

$scope jest obiektem ważnym, tworzy warstwę widoku-modelu i jest odpowiedzialny za bindowanie danych pomiędzy kontrolerem a widokiem. Jego zaśmiecenie powoduje znaczne straty wydajności w działaniu aplikacji. Odpowiednie grupowanie atrybutów i podpinanie ich jako atrybutów obiektów pomaga w utrzymaniu porządku.

W żadnym wypadku nie możemy uszkodzić albo nadpisać zakresu, możemy jedynie dodawać do niego atrybuty i funkcje, które są nam potrzebne. Istnieją też inne sposoby na dbanie o „lekkość” zakresu i zostaną opisane w kolejnych artykułach.

]]>
https://www.p-programowanie.pl/kurs-angular/scope-i-wyrazenia/feed/ 0
Kontrolery i moduły https://www.p-programowanie.pl/kurs-angular/kontrolery-i-moduly/ https://www.p-programowanie.pl/kurs-angular/kontrolery-i-moduly/#respond Thu, 15 Dec 2016 21:32:03 +0000 http://www.p-programowanie.pl/?p=2891 Kontrolery i moduły są podstawowymi bytami w każdej aplikacji AngularJS. Są one odpowiedzialne za podzielenie aplikacji na logiczne warstwy abstrakcji a także za sterowanie jej przepływem. Ucząc się Angulara prawdopodobnie będziesz używał jednego modułu i jednego kontrolera. Ich większa ilość jest potrzebna w momencie tworzenia konkretnej aplikacji, a poprawne zaplanowanie takiej struktury zostanie opisane w kolejnych artykułach.

Główny moduł aplikacji

Moduły aplikacji tworzą abstrakcyjną warstwę aplikacji służącą jedynie do utrzymania porządku w kodzie. Tworząc aplikację AngularJS nie musisz dzielić jej na moduły, jednak wymogiem jest zdefiniowanie przynajmniej jednego tzw. głównego modułu aplikacji, do którego będzie można podpiąć kontrolery.

Do uruchomienia Angulara wystarczy użyć wbudowanej dyrektywy ng-app=”nazwa”. W przypadku podania wartości szuka ona właśnie modułu o takiej nazwie – jest to moduł główny. Na potrzeby kursu będziemy używać tylko jednego modułu, większa ilość przydaje się przy tworzeniu konkretnych aplikacji co zostanie opisane w innym artykule.

Definicja modułu aplikacji sprowadza się do jednej linii kodu:

var app = angular.module("mainModule", []);

Nazwa modułu to mainModule ze względu, że jest to główny moduł aplikacji. Obiekt zostaje przypisany do zmiennej o nazwie app. Oczywiście nazwy możesz dobierać według własnych upodobań. Główny moduł aplikacji opina zasięgiem nasz widok dokładnie w tych atrybutach w jakich został zainicjowany.

Zwróć uwagę, że moduł nie posiada własnego „ciała”. Nie da się zdefiniować w nim zmiennych, funkcji ani umieścić w nim żadnej logiki. Jest tylko abstrakcyjnym elementem, do którego będziemy podpinać nasze kontrolery.

Kontroler aplikacji

Kontroler aplikacji AngularJS jest zwykłą funkcją. Jego instancja zostaje utworzona w momencie napotkania dyrektywy ng-controller=”nazwa” w kodzie HTML. Dyrektywa ng-controller szuka kontrolera o podanej przez nas nazwie, jeżeli go nie znajdzie AngularJS rzuci w konsoli wyjątek.

Definicja przykładowego kontrolera wygląda następująco:

<script>
	var app = angular.module("mainModule", []);

	app.controller('simpleController', function() {
		// ciało kontrolera
	});
</script>

<body ng-app="mainModule" ng-controller="simpleController">
	Angular start {{3+2}}
</body>

W powyższym przykładzie do utworzenia kontrolera użyta została funkcja anonimowa. Jest to rozwiązanie najczęściej spotykane w różnych artykułach i kursach. Wydaje się w miarę proste i logiczne. Mimo to zachęcam Cię, aby trzymać się konwencji, którą zaprezentuję poniżej:

<script>
	var app = angular.module("mainModule", []);

	app.controller('simpleCtrl', simpleController);
	
	function simpleController() {
		//ciało kontrolera
	}
</script>

<body ng-app="mainModule" ng-controller="simpleCtrl">
	Angular start {{3+2}}
</body>

Funkcja kontrolera została zadeklarowana jako normalna nazwana funkcja a nie funkcja anonimowa. Dzięki temu definicje kontrolerów nie przeplatają się z ich deklaracjami co ma ogromne znaczenie w przyszłości, gdy będziesz tworzył średniej wielkości aplikacje. Taka konwencja jest też bardziej czytelna w momencie kiedy zaczniemy korzystać z mechanizmu wstrzykiwania zależności (opisany w innym artykule).

Kontroler aplikacji jest warstwą, która zawiera w sobie $scope (z ang. przestrzeń/zasięg). Każdy kontroler ma swój własny $scope, do którego przypinamy różne atrybuty oraz metody widoczne później w widoku. $scope tworzy warstwę widok-model wzorca MVVM i moim zdaniem jest nieprzetłumaczalna na język polski. Z tego względu do końca serii będę posługiwał się tym pojęciem.

Oto przykładowa struktura aplikacji:

Gdy w kolejnych lekcjach do kontrolerów a konkretniej do $scope danego kontrolera będziemy podpinać zmienne i metody, ich zasięg będzie taki jak na rysunku wyżej. Kontroler zainicjalizowany dyrektywą ng-controller ma dostęp tylko do danych widoku elementów drzewa DOM które są mu podrzędne. Kontrolery nie mogą wymieniać między sobą danych ani wpływać na swój stan (przynajmniej bez zastosowania dodatkowych mechanizmów).

Oczywiście możliwe jest podpięcie jednego kontrolera na całą gałąź atrybutu body a nawet html. Wtedy moduł i kontroler opinałby swoim zasięgiem całą stronę.

]]>
https://www.p-programowanie.pl/kurs-angular/kontrolery-i-moduly/feed/ 0
Szkielet aplikacji https://www.p-programowanie.pl/kurs-angular/szkielet-aplikacji/ https://www.p-programowanie.pl/kurs-angular/szkielet-aplikacji/#respond Thu, 15 Dec 2016 18:38:41 +0000 http://www.p-programowanie.pl/?p=2881 AngularJS to platforma programistyczna w całości napisana w języku JavaScript. Aby móc tworzyć aplikację opartą o AngularJS niezbędne jest zaimportowanie pliku źródłowego Angulara. Tak jak większość plików JavaScriptowych można go doczytywać lokalnie lub dynamicznie z zewnętrznego serwera. Informacje o wszystkich wersjach znajdziesz wchodząc na stronę www.angularjs.org.

Wersje AngularJS

Na dzień dzisiejszy wersje Angular prezentują się następująco:

  • wersja 1.6 – testowa
  • wersja 1.5 – stabilna produkcyjna
  • wersja 1.2 – stara wersja wspierająca m.in. IE8, nie polecana do zastosowań produkcyjnych

Wersja 1.2 jest kopią starej wersji AngularJS, która jeszcze daje radę integrować się ze starymi przeglądarkami takimi jak IE8. Są na nią nanoszone wszelkie poprawki bezpieczeństwa bez żadnych dodatkowych ulepszeń.

Tworząc aplikację komercyjną/produkcyjną używanie wersji testowej jest ryzykowane, ponieważ nie ma pewności czy zmiany jakie w niej zachodzą zostaną ostatecznie włączone do głównej stabilnej wersji frameworka. W kursie nie musimy się tym martwić.

Pobieranie AngularJS

Pisząc projekt AngularJS z prawdziwego zdarzenia powinniśmy posługiwać się narzędziami służącymi do automatyzacji pracy takimi jak NodeJS czy Grunt. Są to narzędzia znane każdemu programiście front-endu. Pomagają one przebudowywać projekt, instalować i usuwać dodatkowe biblioteki takie jak AngularJS, dbają o posiadanie najnowszych wersji bibliotek a także zaciemniają kod (obfuskacja) i minifikują go. Na ten temat pojawi się na pewno osobny wpis.

W niniejszym kursie będę używał AngularJS wczytywanego dynamicznie z zewnętrznego serwera z pod adresu, jednak nic nie stoi na przeszkodzie aby pobrać AngularJS poprzez narzędzie npm dołączone do NodeJS.

https://ajax.googleapis.com/ajax/libs/angularjs/1.6.0/angular.js

Niezbędne narzędzia dla programisty AngularJS

Aby zacząć tworzyć aplikacje oparte o platformę AngularJS potrzebujemy trzech podstawowych rzeczy:

  • przeglądarki Google Chrome
  • wtyczki ngInspector
  • pliku *.html z podstawowym szkieletem strony z dołączonym skryptem AngularJS (link wyżej)

Przez długi czas programowałem w AngularJS używając przeglądarki Firefox, jednak jest to niewygodne. Google niesamowicie rozbudował możliwości Chrome szczególnie jeśli chodzi o narzędzia dla programistów. Chrome posiada lepsze możliwości debugowania kodu oraz wbudowany klient RESTa.

Momentem przełomowym kiedy przestałem używać przeglądarki Firefox do programowania było wprowadzenie przez Mozillę zabezpieczenia blokującego używanie nieautoryzowanych wtyczek. Przez to, nie byłem wstanie używać dodatku ngInspector dla Firefoxa, a jest to narzędzie nieocenione.

NgInspector jest prostym narzędziem pozwalającym przeglądać modele kontrolerów aplikacji AngularJS. Co prawda, wartości zmiennych można wyświetlić w konsoli deweloperskiej jednak jest to operacja nieznacznie dłuższa, a programista musi znać nazwę i lokalizację zmiennej, którą chce wyświetlić. Dzięki używaniu ngInspectora mamy podgląd na całą strukturę modelu ze wszystkimi zagnieżdżeniami i wartościami.

Podstawowy szkielet aplikacji

Oto treść pliku, która może być używana przez Ciebie jako szkielet do pisania pierwszych aplikacji w AngularJS:

<!DOCTYPE html>
<html lang="pl-PL">

<head>
	<meta http-equiv="content-type" content="text/html; charset=utf-8" />
	<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.0/angular.js"></script>
</head>

<body ng-app>
	Angular start {{3+2}}
</body>
	
</html>

Po uruchomieniu pliku w przeglądarce Chrome na ekranie powinien pojawić się napis „Angular start 5”. Jeżeli tak się nie stało, informacji o błędach szukaj w konsoli deweloperskiej. Wyświetlają się tam wszystkie błędy aplikacji.

Do tagu <body> została dołączona dyrektywa ng-app. AngularJS parsując drzewo DOM dokumentu szuka jej ponieważ jest to dyrektywa startowa dla aplikacji angularowych. W momencie jej znalezienia zaczyna działać Angular. Podwójne nawiasy klamrowe są tzw. wyrażeniem i zostają automatycznie obliczone, dlatego zwracają wynik dodawania.

W przypadku braku dyrektywy ng-app nawiasy klamrowe nie zostaną potraktowane jako wyrażenie AngularJS i wyświetlą się jako zwykł tekst.

]]>
https://www.p-programowanie.pl/kurs-angular/szkielet-aplikacji/feed/ 0
Wzorzec projektowy MVVM https://www.p-programowanie.pl/kurs-angular/wzorzec-projektowy-mvvm/ https://www.p-programowanie.pl/kurs-angular/wzorzec-projektowy-mvvm/#comments Sat, 03 Dec 2016 23:31:27 +0000 http://www.p-programowanie.pl/?p=2864 Niezmierną zaletą platformy programistycznej AngularJS jest możliwość łatwego dzielenia aplikacji na warstwy. Podstawową jednostką grupującą aplikację jest moduł, skupia on w sobie różne kontrolery. Kontrolery posiadają osobne przestrzenie i zakresy (o czym będzie w późniejszym rozdziale). Kontroler jest w stanie bindować dane z widokiem i tutaj pojawia się główny problem: w wielu źródłach jest napisane, że programując w Angularze tworzymy aplikację w architekturze MVC – nic bardziej mylnego. Mimo, że występują tutaj widoki, kontrolery i modele porównanie architektury aplikacji AngularJS z architekturą MVC jest nieprawidłowe, wręcz błędne.

Działanie architektury MVC

W architekturze MVC punktem wejścia do systemu jest kontroler aplikacji. Kontroler jest bezstanowym obiektem odpowiedzialnym za logikę. Przetwarza on żądanie i przekazuje do widoku odpowiedni model wypełniony danymi. Bardzo ważnym faktem jest to, że kontroler musi posiadać referencję zarówno do widoku jak i do modelu danych.

mvc

Kontroler jako punkt wejścia wie o widoku oraz o modelu z jakiego będzie korzystał. Związanie modelu i widoku następuje już podczas wywołania danej akcji kontrolera. Doskonałym przykładem takiej infrastruktury są np. aplikacje napisane w ASP.NET MVC.

AngularJS i architektura MVVM

Aplikacje napisane w AngularJS nie spełniają warunków architektury MVC. Są tworzone w architekturze MVVM czyli „model widok widok-model” (z ang. model view view-model). Odejście od MVC jest spowodowane rozwojem frontendu. MVC jest typowo bezstanowy, każda akcja posiada swoją własną ścieżkę w postaci adresu URL. Kiedy frontend zrobił się ładniejszy, a co za tym idzie bardziej zaawansowany, a co za tym idzie cięższy, pojawiła się konieczność ulepszenia wzorca MVC. Pewną dogodnością było używanie zapytań AJAX jednak rodziły one tyle samo problemów co korzyści. W MVVM warstwę bezstanowego kontrolera zamieniono na warstwę widok-modelu, która była już w stanie zapamiętać i przetwarzać dane (nie była bezstanowa).

W AngularJS punktem wejścia do systemu nie są kontrolery tylko widoki. Najdrobniejsze zmiany widoku od razu wpływają na stan systemu, bez wysyłania dodatkowych żądań. Dzieje się to dzięki mechanizmowi dwustronnego bindowania danych. Zmiana wartości w modelu (lub po prostu zmiennej) automatycznie zostanie wyświetlona użytkownikowi. To samo  w drugą stronę, zmiana wartości w formularzu przez użytkownika od razu zostanie zauważona w modelu danych. Istotne jest to, że widok jako warstwa, nie jest w żaden sposób związana z modelem danych. Łączy je ze sobą widok-model. Nie jest to już warstwa bezstanowa w odróżnieniu od kontrolera w MVC.

mvvm

Jak widać w Angularowej architekturze MVVM nie ma kontrolera mimo, że istnieje on jako jedna z warstw tej platformy. Angularowy kontroler sam w sobie jest niczym, jest jedynie dodatkową warstwą abstrakcji z których zbudowane są moduły. Moduły i kontrolery pomagają utrzymywać porządek w kodzie ale nic poza tym. Kontroler z Angulara można porównać z klasą w języku C# – żadnego związku z MVC ona nie posiada.

Warstwę kontrolera znaną z MVC w AngularJS pełni widok-model a w jego skład wchodzi zakres (czyli $scope) oraz mechanizm dwustronnego bindowania danych. Jeżeli nie wiesz czym one są, dowiesz się tego w kolejnych lekcjach.

Kompromis architektoniczny czyli MVW

Rozważania na temat stylów projektowania aplikacji często rodzą więcej pytań niż odpowiedzi. Ciężko zestawić ze sobą dwie architektury i doszukiwać się miedzy nimi różnic i podobieństw. Wielu mądrych ludzi ma różne opinie na temat klasyfikacji poszczególnych platform. W celu znalezienia kompromisu istnieje także architektura „model widok cokolwiek” (z ang. model view whatever). Doskonale pasuje ona do aplikacji stworzonych w AngularJS.

Na koniec tego artykułu warto podsumować: we wzorcu MVC źródłem wejścia do systemu jest kontroler i to on binduje widok z modelem a więc posiada referencje do nich. We wzorcu MVVM źródłem wejścia jest widok, i nie ma on żadnego pojęcia o modelach istniejących w systemie. Jakkolwiek nie sklasyfikujesz swojej aplikacji lub jakiejkolwiek roli nie przypiszesz kontrolerowi z platformy AngularJS warto zapamiętać – nie jest to architektura MVC.

]]>
https://www.p-programowanie.pl/kurs-angular/wzorzec-projektowy-mvvm/feed/ 1
Czym jest AngularJS? https://www.p-programowanie.pl/kurs-angular/czym-jest-angularjs/ https://www.p-programowanie.pl/kurs-angular/czym-jest-angularjs/#comments Sat, 03 Dec 2016 18:38:21 +0000 http://www.p-programowanie.pl/?p=2852 Ze względu na brak informacji w polskim internecie na temat świetnej biblioteki jaką jest AngularJS, postanowiłem spróbować własnych sił i napisać takowy. Jest to pierwsza seria wpisów w stylu kursu jaką umieszczę na tym blogu. Zaletą kursu jest to, że skupia odpowiednią ilość danych w jednym miejscu, a przede wszystkim odpowiednie wpisy są ułożone w logiczną całość.

Szybki rozwój frontendu

Kilka lat temu dobry frontendowiec musiał umieć stworzyć ładnie wyglądający szablon. Była to osoba w dużej mierze zajmująca się graficznym projektem strony. W momencie gdy szablon był gotowy, do akcji wkraczał np. programista PHP. Dodawał on odpowiedni kod, dodając do witryny całą funkcjonalność.

W ostatnim czasie frontend rozwinął się bardzo silnie. Internet przejął bardzo duży fragment rynku handlowego i usługowego. Dzisiaj biznes, który nie istnieje w internecie, nie istnieje wcale. Dzięki temu narodziło się wiele gałęzi biznesu zajmującymi się aplikacjami internetowymi, pozycjonowaniem i wirtualną reklamą.

Rozwój aplikacji internetowych wymusił rozwój technologii, w których są one tworzone. Pojawił się nowe języki programowania lepsze od języka PHP, w których tworzone są serwisy obsługujące miliony klientów dziennie. Ciekawe jest to, że niesamowicie rozwinął także się sam JvaScript.

Ewolucja JavaScriptu

Kiedy uczyłem się programować w szkole podstawowej, język skryptowy JavaScript kojarzony był tylko z niepotrzebnymi komponentami dołączanymi do stron WWW. Jeżeli drogi czytelniku sięgnąłbyś do książek powiedzmy z 2003 roku, w połowie z nich JavaScript opisywany byłby jak wirus i najgorsze zło, którego trzeba unikać.

Wielu ekspertów twierdziło, że JavaScript nie ma racji bytu ponieważ wykonywany jest po stronie przeglądarki, co za tym idzie użytkownik może wyłączyć jego obsługę. Co za tym idzie niemożliwym było wykorzystanie JavaScriptu to żadnych poważnych zadań. Ostatecznie język ten wykorzystywany był np. w połączeniu z AJAXem w celu asynchronicznego wyczytywania danych.

Aktualnie JavaScript jest numerem jeden w budowaniu warstwy klienckiej wszelakich aplikacji internetowych. Ponieważ pisanie w czystym JavaScripcie było złożone i generowało dużą ilość kodu, zaczęły się rozwijać platformy programistyczne mające na celu przyśpieszyć ten proces. Powstało ich całkiem sporo i są ciągle rozwijane a jednym z nich jest AngularJS.

Platforma programistyczna AngularJS

AngularJS to platforma programistyczna języka JavaScript rozwijana przez firmę Google. Angular jest całkiem darmowy także do zastosowań komercyjnych. Cechuje go wszystko to co wszelkie inne platformy programistyczne. Bardzo szybko da się w nim napisać skomplikowaną na pozór aplikację – występuje w nim dwustronne bindowanie wartości zmiennych. AngularJS dobrze sprawdza się także w rozdzieleniu logiki aplikacji od warstwy prezentacji. Stosując go backend nie zajmuje się generowaniem kodu HTML tylko dostarczeniem surowych danych np. za pomocą REST.

AngularJS służy do tworzenia stron SPA (single page applications). Oznacza to, że pierwsze żądanie i pierwsze wejście do aplikacji internetowej powinno dostarczyć wszelkie wymagane pliki arkusza stylów oraz szablonu. Reszta danych wczytywana jest asynchronicznie z backendu w zależności od kontekstu i dynamicznie prezentowana.

Gdzie nie używać AngularJS

AngularJS powinien być przede wszystkim wykorzystywany podczas tworzenia zaawansowanych aplikacji, w których ważniejsze jest zapewnienie dużej ilości funkcji i logiki niż statycznych stron z dużą ilością tekstu. Musisz wiedzieć o tym, że wyrażenia Angularowe służące do wyświetlania danych nie są przetwarzane przez przeglądarki internetowe, dlatego pozycjonowanie aplikacji Angularowych jest wręcz niemożliwe bez dodatkowych sztuczek i bibliotek.

Stąd AngularJS doskonale sprawdza się w sytuacjach takich jak aplikacje bankowe, panele administracyjne, zaawansowane kalkulatory i aplikacje przetwarzające rozbudowane formularze HTML. Totalnie nie sprawdza się natomiast na blogach, serwisach informacyjnych i innych stron z dużą ilością tekstu i artykułów.

]]>
https://www.p-programowanie.pl/kurs-angular/czym-jest-angularjs/feed/ 5